1
+ using AttribDoc . Attributes ;
2
+ using Bunkum . Core ;
3
+ using Bunkum . Core . Endpoints ;
4
+ using Bunkum . Core . Responses ;
5
+ using Bunkum . Listener . Protocol ;
6
+ using Refresh . Core . Configuration ;
7
+ using Refresh . Core . Types . Categories ;
8
+ using Refresh . Core . Types . Data ;
9
+ using Refresh . Database ;
10
+ using Refresh . Database . Models . Authentication ;
11
+ using Refresh . Database . Models . Levels ;
12
+ using Refresh . Database . Models . Users ;
13
+ using Refresh . Database . Query ;
14
+ using Refresh . Interfaces . APIv3 . Documentation . Attributes ;
15
+ using Refresh . Interfaces . APIv3 . Endpoints . ApiTypes ;
16
+ using Refresh . Interfaces . APIv3 . Endpoints . ApiTypes . Errors ;
17
+ using Refresh . Interfaces . APIv3 . Endpoints . DataTypes . Response . Categories ;
18
+ using Refresh . Interfaces . APIv3 . Endpoints . DataTypes . Response . Levels ;
19
+ using Refresh . Interfaces . APIv3 . Endpoints . DataTypes . Response . Users ;
20
+ using Refresh . Interfaces . APIv3 . Extensions ;
21
+
22
+ namespace Refresh . Interfaces . APIv3 . Endpoints ;
23
+
24
+ public class CategoryApiEndpoints : EndpointGroup
25
+ {
26
+ [ ApiV3Endpoint ( "levels" ) , Authentication ( false ) ]
27
+ [ ClientCacheResponse ( 1800 ) ] // cache for half an hour
28
+ [ DocSummary ( "Retrieves a list of categories you can use to search levels" ) ]
29
+ [ DocQueryParam ( "includePreviews" , "If true, a single level will be added to each category representing a level from that category. False by default." ) ]
30
+ [ DocError ( typeof ( ApiValidationError ) , "The boolean 'includePreviews' could not be parsed by the server." ) ]
31
+ public ApiListResponse < ApiLevelCategoryResponse > GetLevelCategories ( RequestContext context , CategoryService categories ,
32
+ DataContext dataContext )
33
+ {
34
+ bool result = bool . TryParse ( context . QueryString . Get ( "includePreviews" ) ?? "false" , out bool includePreviews ) ;
35
+ if ( ! result ) return ApiValidationError . BooleanParseError ;
36
+
37
+ IEnumerable < ApiLevelCategoryResponse > resp ;
38
+
39
+ // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
40
+ if ( includePreviews ) resp = ApiLevelCategoryResponse . FromOldList ( categories . LevelCategories , context , dataContext ) ;
41
+ else resp = ApiLevelCategoryResponse . FromOldList ( categories . LevelCategories , dataContext ) ;
42
+
43
+ return new ApiListResponse < ApiLevelCategoryResponse > ( resp ) ;
44
+ }
45
+
46
+ [ ApiV3Endpoint ( "levels/{route}" ) , Authentication ( false ) ]
47
+ [ DocSummary ( "Retrieves a list of levels from a category" ) ]
48
+ [ DocError ( typeof ( ApiNotFoundError ) , "The level category cannot be found" ) ]
49
+ [ DocUsesPageData ]
50
+ [ DocQueryParam ( "game" , "Filters levels to a specific game version. Allowed values: lbp1-3, vita, psp, beta" ) ]
51
+ [ DocQueryParam ( "seed" , "The random seed to use for randomization. Uses 0 if not specified." ) ]
52
+ [ DocQueryParam ( "players" , "Filters levels to those accommodating the specified number of players." ) ]
53
+ [ DocQueryParam ( "username" , "If set, certain categories like 'hearted' or 'byUser' will return the levels of " +
54
+ "the user with this username instead of your own. Optional." ) ]
55
+ public ApiListResponse < ApiGameLevelResponse > GetLevels ( RequestContext context , CategoryService categories , GameUser ? user ,
56
+ [ DocSummary ( "The name of the category you'd like to retrieve levels from. " +
57
+ "Make a request to /levels to see a list of available categories" ) ]
58
+ string route , DataContext dataContext )
59
+ {
60
+ if ( string . IsNullOrWhiteSpace ( route ) )
61
+ {
62
+ return new ApiError ( "You didn't specify a route. " +
63
+ "You probably meant to use the `/levels` endpoint and left a trailing slash in the URL." , NotFound ) ;
64
+ }
65
+
66
+ ( int skip , int count ) = context . GetPageData ( ) ;
67
+
68
+ DatabaseList < GameLevel > ? list = categories . LevelCategories
69
+ . FirstOrDefault ( c => c . ApiRoute . StartsWith ( route ) ) ?
70
+ . Fetch ( context , skip , count , dataContext , new LevelFilterSettings ( context , TokenGame . Website ) , user ) ;
71
+
72
+ if ( list == null ) return ApiNotFoundError . Instance ;
73
+
74
+ DatabaseList < ApiGameLevelResponse > levels = DatabaseListExtensions . FromOldList < ApiGameLevelResponse , GameLevel > ( list , dataContext ) ;
75
+ return levels ;
76
+ }
77
+
78
+ [ ApiV3Endpoint ( "users" ) , Authentication ( false ) ]
79
+ [ ClientCacheResponse ( 1800 ) ] // cache for half an hour
80
+ [ DocSummary ( "Retrieves a list of categories you can use to search users. Returns an empty list if the instance doesn't allow showing online users." ) ]
81
+ [ DocQueryParam ( "includePreviews" , "If true, a single user will be added to each category representing a user from that category. False by default." ) ]
82
+ [ DocError ( typeof ( ApiValidationError ) , "The boolean 'includePreviews' could not be parsed by the server." ) ]
83
+ public ApiListResponse < ApiUserCategoryResponse > GetUserCategories ( RequestContext context , CategoryService categories ,
84
+ DataContext dataContext , GameServerConfig config )
85
+ {
86
+ bool result = bool . TryParse ( context . QueryString . Get ( "includePreviews" ) ?? "false" , out bool includePreviews ) ;
87
+ if ( ! result ) return ApiValidationError . BooleanParseError ;
88
+
89
+ if ( ! config . PermitShowingOnlineUsers ) return new ApiListResponse < ApiUserCategoryResponse > ( [ ] ) ;
90
+ IEnumerable < ApiUserCategoryResponse > resp ;
91
+
92
+ if ( includePreviews ) resp = ApiUserCategoryResponse . FromOldList ( categories . UserCategories , context , dataContext ) ;
93
+ else resp = ApiUserCategoryResponse . FromOldList ( categories . UserCategories , dataContext ) ;
94
+
95
+ return new ApiListResponse < ApiUserCategoryResponse > ( resp ) ;
96
+ }
97
+
98
+ [ ApiV3Endpoint ( "users/{route}" ) , Authentication ( false ) ]
99
+ [ DocSummary ( "Retrieves a list of users from a category." ) ]
100
+ [ DocError ( typeof ( ApiNotFoundError ) , "The user category cannot be found, or the instance does not allow showing online users." ) ]
101
+ [ DocUsesPageData ]
102
+ [ DocQueryParam ( "username" , "If set, certain categories like 'hearted' will return the related users of " +
103
+ "the user with this username instead of your own. Optional." ) ]
104
+ public Response GetUsers ( RequestContext context , CategoryService categories , GameUser ? user ,
105
+ [ DocSummary ( "The name of the category you'd like to retrieve users from. " +
106
+ "Make a request to /users to see a list of available categories" ) ]
107
+ string route , DataContext dataContext , GameServerConfig config )
108
+ {
109
+ // Bunkum usually routes users/me requests to here aswell, so use this hack to serve those requests properly.
110
+ if ( route == "me" )
111
+ {
112
+ if ( user == null ) return ApiAuthenticationError . NotAuthenticated ; // Error documented in UserApiEndpoints.GetMyUser()
113
+ return new Response ( new ApiResponse < ApiExtendedGameUserResponse > ( ApiExtendedGameUserResponse . FromOld ( user , dataContext ) ! ) , ContentType . Json ) ;
114
+ }
115
+
116
+ if ( string . IsNullOrWhiteSpace ( route ) )
117
+ {
118
+ return new ApiError ( "You didn't specify a route. " +
119
+ "You probably meant to use the `/users` endpoint and left a trailing slash in the URL." , NotFound ) ;
120
+ }
121
+
122
+ if ( ! config . PermitShowingOnlineUsers ) return ApiNotFoundError . Instance ;
123
+ ( int skip , int count ) = context . GetPageData ( ) ;
124
+
125
+ DatabaseList < GameUser > ? list = categories . UserCategories
126
+ . FirstOrDefault ( c => c . ApiRoute . StartsWith ( route ) ) ?
127
+ . Fetch ( context , skip , count , dataContext , user ) ;
128
+
129
+ if ( list == null ) return ApiNotFoundError . Instance ;
130
+
131
+ ApiListResponse < ApiGameUserResponse > users = DatabaseListExtensions . FromOldList < ApiGameUserResponse , GameUser > ( list , dataContext ) ;
132
+ return new Response ( users , ContentType . Json ) ;
133
+ }
134
+ }
0 commit comments