Pterodactyl Panel (Android App)
- 61 Devlogs
- 157 Total hours
An app to manage https://pterodactyl.io. Pterodactyl is a server management panel which runs each server in an isolated docker container
An app to manage https://pterodactyl.io. Pterodactyl is a server management panel which runs each server in an isolated docker container
com.google.android.play:app-update libraryfastlane folderAdded optional biometric authentication so people can lock their app behind phone code or biometric verification (like fingerprint etc.)
getNestEgg function (eggid -> eggId)navController.popBackStack() (which basically acts as the back button) i specifically navigate to generic screen (eg. “user” -> “users”) and remove current screen from the back history so in the case that there is no screen to back into (eg if the app is opened via a deep link or stuff like that) it will go to the generic tab anyway)SUCCESS (green) and WARNING (yellow)As a prize for your great work, look out for a bonus prize in the mail :)
modifier param to the Container componentContainer components are spaced in the Build Configuration tab and create server popuprefreshIndex variable before the main resource actually refreshed and this caused the tabs not to refresh unless the user pulled to refresh twiceCodeText component now uses default text style & font weight instead of pre-defined ones (for some reasons with pre-defined ones, in the ScrollableTabRow component it adds like a margin on the bottom of the blue active indicator)UpdateServerDetailsBody to mark required fields as non-nullablealias:port when an alias is available, otherwise will default to ip:port
ip:port text to a variable to avoid writing the same thing twicesearchable property in the Select component that adds a text input to filter the options*Include enums are now chainable so doing ListNodesQueryInclude.SERVERS + ListServersQueryInclude.USER returns servers.user<enum>.toQueryString(...) (like ListServersQueryInclude.toQueryString(...)) i do listOf(...).toQueryString()
ApiErrorResponse in a models folder for better organizationthis is a code only change so ig i’ll show pics of the code
"quoted" test to actual inline code blocks
CodeText component now uses CharSequence instead of String so it also supports annotated stringsText to CodeText
400 other than checking the error message (ptero got no way of knowing if 2FA is enabled other than that
)TextInput component to allow showing optional suggestions (used in the assign new allocations popup, IP Address input)Checkbox component now is more transparent when disabled (from 0.5 to 0.2 alpha)Pager componentTable component to add a param to disable lazy loading (for some reason it was laggier)CodeText component to CodeBlockText and added params to highlight the content with a specific languageCodeText component to have both normal text and inline code text (kinda like markdown when you do this)updateNode function’s body & params since some required params were marked as nullableApiService instead of having them in the ApplicationApiService and added 2 endpoints to respectively fetch system data in v1 and v2Popup component now has a scrollbararrangement property in the Table’s headers & row cells so that i can center certain thingsTextInput and Switch components to make their description text color customizable.aab file to play store after it’s built and made the release (hopefully, i didn’t test it)build.gradle.kts file (so debug build get the (Debug) suffix)CodeText component to display text as code (kinda like this) and applied it in all the places where I previously just copy pasted same codeTable component, originally from my Zipline apponRefresh it didn’t actually set isRefreshing to true so the spinner would immediately disappear instead of swithcing to the spinner state till it finished loadingpage property from functions since the page is already stored in the viewModel so it made no sense to retrieve it from the viewModel just to send it to a function in the viewModel
formatDate function, it was crashing the app on older Android versions (<= 13) because the date time parser couldn’t understand the timezone (it expected a date like 2026-08-08-T15:43:30Z instead of 2026-08-08-T15:43:30+02:00)TextInputs being marked as singleLine
README
also did some tests with downloading but i failed so i just undid them without committing or anything
eventDescription variable in the ActivityDisplay component (i didn’t even know that was possible, i found it out cuz i made a _count variable and android studio suggested me to put it directly in the when, lmao)1.0 -> 1.0.0)[build] or [release]
*Scrollbar modifiers above any padding modifier so they look betterScrollableTabRow to a normal Row instead of LazyRow since it’ll never have enough items to cause any lag
, now i just gotta finish the small importance TODOs like removing the padding to the scrollbar in most scrollable views & stuff like that)ActivityDisplay in the general components as it’s used in the accoutn activity tooActivityDisplay to include the user agent button when available
)formatDate function to include a param to toggle the year in the date
)refreshIndex += 1 with refreshIndex++
/disable at the end and i was wondering why 2FA wasn’t getting disabled even tho the request succeeded
(without /disable it acts as enable endpoint))
… shitty API fr)Column instead of a LazyColumn as lazily loading the server was causing more lag than loading all at once (after all it’s just 10 servers per page).total) instead of the total number of pages (.totalPages), idk how i missed that
)LazyColumn & put the permissions in a remember to hopefully try to decrease lagI wanted to make a screen rec but i was too lazy to then go and censor my 2FA secret 
:D) along with a function to parse the variables in the logs’ descriptionHeader and ScrollableTabRow componentsAccountActivity to ActivityLog as it’s used in server activity too
).trim() to all user inputsPullToRefreshBox component that just uses Material3 PullToRefreshBox under the hood, i just made a component so i can easily style it as it’s in 1 single placehasPermission to check if an user has a certain permission instead of repeating the same code like 20 timesStartupContainer to a more general Container
ServerStatus enum to Server.Status
navController from all tabs as it was unusedI also tried to make the TextInput label not squared for a while but failed 😭 , for some reasons even if i clip my highest component to rounded corners, it still has the squared bg in the back 
String instead of Long
Column
After i tested more the additions of the last devlog i found some issues so here i fixed them
.gz instead of .tar.gz
enabled param in the BackupDisplay as it was basically uselessFile to FileDisplay for consistency with other componentsString instead of ServerSubuser.Permission
deleteServerBackup function which was marked as GET instead of DELETE
truncate param required in the restoreServerBackup function as it’s required in the APILabeledCheckbox componentI legit think Android Studio is ragebaiting me, as soon as i start the rec the app starts to lag, when i stop the rec it’s fine
only_when_online property in the ServerSchedule data classString instead of Long for the IDs)ScrollableTabRow not using all the horizontal space when in landscape modePopup calls at the top of screen/tab filesformatDate function from my Zipline appViewModel for better ordering instead of putting everything is a generic onereturns in some places instead of many if else blocksScrollableTabRow component improperly placing icons when there was no textdata classes inside each other so i can do Server.Attributes.Limits instead of having a class with a long name like ApplicationServerAttributesLimits
rotateServerDatabasePassword and deleteServerDatabase, they were using the wrong endpoint (/server instead of /servers)IconButton component from my Zipline app and slightly modified it (the MoreActionsButton component now uses that)Notification object component’s background color.env fileswriteServerFile having a wrong Content-Type (it had application/json instead of text/plain since the input is a raw string)> part of the tagTextInput component to have a fontFamily property so that in the core editor i can set the text to be monospace(Sorry for the slightly laggy video, not sure if it’s cuz it’s via a emulator or cuz i compressed the video from 136 MiB down to 36 MB)
File component’s “more actions” menu (before it was empty)uploadServerFiles to be able to upload multiple files at oncedrwxr-xr-x) to int/octal (like 755)Popup component and Storage object form my Zipline appSecureStorage class to add the file download folder variableFile component to avoid repeating same codesurfaceContainer used in notificationsConsole tab instead of the general server screen, this way it auto reconnects when the user switches back to the console tab (since it disconnects when the user moves to another tab to save resources)Button component
)formatMs function from my zipline app to convert ms to human readable time like “1h 30m 20s” for the server uptimeWebSocketManager class renamed the setPowerState function to sendPowerSignal as it makes more senseOn the console screen i wanted the console to follow its actual colors but only lib i found to do that doesn’t support jetpack compose that well so i had to rely on the XML’s TextView (but in compose thanks to the AndroidView component)
object to easily access the WebSocketManager from a ViewModel classCurrently working on the specific server page, right now it’s empty but i got websocket working, i’ll push it once it’s finished
listServers not working cuz of wrong path (i didn’t know how to tell retrofit to use just the baseUrl, then after it threw an error i googled it and realized i just had to put .)getServerResources response class, now the server state uses a enum instead of a generic stringServer class to better fix some typed and add 1 missing keyuploadServerFile HTTP method (i didn’t do i it initially cuz the docs were confusing and i couldn’t understand how to select the upload directory, turns out i was setting the directory query on the request to get the signed url instead of the signed url itself 😭shimmerable modifier from my Zipline app to make skeleton loadingsuspend functions so i fixed itNotification object from my Zipline app, tho i gotta fix it a bit cuz the color is kinda ugly and i still don’t know which one in the palette to use cuz imo none of them fits, maybe i’ll just use a separate color out of the palette for itTextInput component to add a optional required field which when set to true adds a red * next to the label, might do this with switches toonow i gotta do the frontend part
Finished creating the methods to call the application API (basically just copy pasted same code and changed the method & the fuction vars)
Now i gotta make the same thing for the client API
Header component from one of my other appsFinished adding the application API HTTP requests (added application/servers/{serverId}/databases, application/nests and application/nests/{nestId}/eggs)
Also fixed some classes having wrong data cuz i realized that i copy pasted them without actually changing their name in some places 😭
you can see them in the commit here
even tho i’m kinda confused cuz in the docs i didn’t see any endpoint to create or manage nests, eggs and database hosts
Added more application API HTTP requests (all the application/nodes/{nodeId}/allocations, application/locations and application/servers endpoints)
you can see them in the commit here
Added more application API HTTP requests (all the /application/user and /application/nodes endpoints)
you can see them in the commit here
application and client (as pterodactyl handles those differently with 2 diff api keys)Added more HTTP requests (ngl am too lazy to list them all, just watch them in the commit here)
Added even more HTTP requests, tho could be not 100% accurate cuz ptero API is shit 😭, the official one is AI generated and has hallucinations….
anyway i added:
GET /api/client/account/activityGET /api/client/account/ssh-keysPOST /api/client/account/ssh-keysPOST /api/client/account/ssh-keys/removeGET /api/client/servers/{identifier}GET /api/client/servers/{identifier}/resourcesPOST /api/client/servers/{identifier}/commandPOST /api/client/servers/{identifier}/powerGET /api/client/servers/{identifier}/databasesPOST /api/client/servers/{identifier}/databasesPOST /api/client/server/{serverIdentifier}/databases/{databaseIdentifier)/rotate-passwordDELETE /api/client/server/{serverIdentifier}/databases/{databaseIdentifier}GET /api/client/servers/{identifier}/files/listGET /api/client/servers/{identifier}/files/contentsGET /api/client/servers/{identifier}/files/downloadPOST /api/client/servers/{identifier}/files/writePOST /api/client/servers/{identifier}/files/create-folderPOST /api/client/servers/{identifier}/files/copyPUT /api/client/servers/{identifier}/files/renamePOST /api/client/servers/{identifier}/files/compressPOST /api/client/servers/{identifier}/files/decompressPOST /api/client/servers/{identifier}/files/deleteGET /api/client/servers/{identifier}/files/uploadPOST /api/client/servers/{identifier}/files/chmodGET /api/client/servers/{identifier}/schedulesGET /api/client/servers/{identifier}/schedules/{scheduleIdentifier}POST /api/client/servers/{identifier}/schedulesPOST /api/client/servers/{identifier}/schedules/{scheduleIdentifier}DELETE /api/client/servers/{identifier}/schedules/{scheduleIdentifier}POST /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasksPOST /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks/{taskIdentifier}DELETE /api/client/servers/{serverIdentifier}/schedules/{scheduleIdentifier}/tasks/{taskIdentifier}Writing all these took alot of time
Started adding some HTTP requests typedef, still missing alot tho
for now i added:
GET /api/clientGET /api/client/permissionsGET /api/client/accountGET /api/client/account/two-factorPOST /api/client/account/two-factor (enable)POST /api/client/account/two-factor (disable)PUT /api/client/account/emailPUT /api/client/account/passwordGET /api/client/account/api-keysDELETE /api/client/account/api-keys/{identifier}POST /api/client/account/api-keysno frontend yet, i’m doing the backend first
Just created the app skeleton based on the other 2 apps i’ve already made (with alot of TODOs for now)
now working on writing the code for HTTP requests to the pterodactyl API and their response types