/*!
@page api API

@tableofcontents

@section api_swag OpenAPI Spec

TGS has a, from code, generated OpenAPI 3.0 specification. It is much more authorative than these documents.

The most up to date version should be found in the most recent appveyor build artifacts. It is also included in release artifacts.

You can use the API explorer SwaggerUI to interact with it: https://tgstation.github.io/tgs-api-doc-host/

@section api_intro Introduction

The TGS API is designed to be a fully realized RESTful service. Once hosted, follow the specified protocol for developing new clients or one off requests that provide full control over the server

Routes and their usages are defined as follows

[I (If Instance is required)] <`Http Method`> "<`Route`>" [Request Model] => <`Response Model`>

@section api_lib Official Libraries

The TGS API's canonical definitions are provided as a .NET Standard library in the form of a nuget package located here: https://www.nuget.org/packages/Tgstation.Server.Api

An all inclusive TAP interface for using the API is also provided in this package: https://www.nuget.org/packages/Tgstation.Server.Client

Last off, if anything in this API doesn't seem to hold true when tested against the server, please open an issue on the repository.

@subsection api_interp Interpreting C# Models

This document will reference the canonical C# models in the @ref Tgstation.Server.Api.Models namespace. Note that these models are built to mirror the JSON requests and responses with a couple caveats.

- The first letter of every field name will/must be lowercase in JSON models
- Fields marked 'Required' should be ignored, this is a semantic for the backing SQL database
- Id fields must be specified when making POST requests
- All other fields are optional and may be absent from responses or requests unless otherwise specified
- Request models can be found in @ref Tgstation.Server.Api.Models.Request. Response models can be found in @ref Tgstation.Server.Api.Models.Response.

@section api_header Headers

TGS expects this set of headers. Failure to provide them will result in 400 error responses

- User-Agent: The user agent product header value of the calling program
- Api: Another product header value representing the version of the API to use. Currently this must be: Tgstation.Server.Api/4.0.0.0

For POST, PATCH, and PUT requests you must also include the content type. Currently only json is supported

- Content-Type: application/json

For requests made to any Instance based API's a header with the Instance's ID must be provided

- Instance: The ID of the instance being accessed

An Authentication header is also required. See @ref api_auth

@section api_response Response Codes

TGS will only every return the response codes listed here

- 200: OK. The response body will contain a json model or array depending on the API called.
- 201: Created. Returned when the request created an entity, 202 trumps this
- 202: Accepted. Used when a response triggers a long running operation such as a @ref Tgstation.Server.Api.Models.Job or server restart
- 204: No Content. Identical to 200 with no response body.
- 400: Bad Request. The response body will contain an @ref Tgstation.Server.Api.Models.ErrorMessage model detailing the error
- 401: Unauthorized. Invalid or expired credentials were provided. Check rights APIs for updates. See @ref api_auth for details
- 403: Forbidden. User tried to make a request they were not allowed to perform.
- 404: Not found. A resource was requested that had never existed. In the case of retrieving a resource by ID, it could potentially exist in the future
- 406: Not Acceptable. Consequence of failing to provide an Accept header
- 408: Request Timeout. The client took to long to continue a request
- 409: Conflict. Documented in the requests that use them
- 410: Gone. Attempted to access/modify a resource that ideally should have been ready, but isn't or no longer is
- 422: Unprocessable Entity: Used specifically when an operation that requires a server restart is unable to be performed due to the @ref Tgstation.Server.Host.Watchdog not being present in the deployment. Should not happen with a proper server configuration. Response body contains an @ref Tgstation.Server.Api.Models.ErrorMessage
- 424: Failed Dependency: When a request that depends on an external API fails for a reason other than rate limiting. The response body will contain an @ref Tgstation.Server.Api.Models.ErrorMessage model detailing the error.
- 429: Rate limited. Used with operations that rely on GitHub.com. If a rate limit is hit for an operation this will be returned. Response will contain a Retry-After header with the amount of seconds to wait.
- 500: Server error. Please report the request and response body to the code repository
- 501: Not implemented. Functionality not available in the current server version
- 503: Service unavailable. The server is either starting up or shutting down and isn't ready to respond to requests. You can try again soon and a response/lack thereof will indicate which of the two events it was

@section api_ver Server Information

The versions of the TGS host can be retireved with this request.

Note: This endpoint does not require authentication

GET "/" => @ref Tgstation.Server.Api.Models.ServerInformation

The Version model fields are based on the C# one and looks like this:

@code{.json}
{
	"version": "<major>.<minor>.<patch>.<revision>"
}
@endcode

Other fields may be present in the Version model but should be ignored. See a description of these version numbers <a href="https://github.com/tgstation/tgstation-server/blob/master/.github/CONTRIBUTING.md#versioning">here</a>.

@section api_auth Authentication

Every request made to TGS requires authentication. It is provided in the form of the Authorization header.

The first request made to TGS must be to login the user

POST "/" => @ref Tgstation.Server.Api.Models.Token

Headers:

- Authorization:Basic `<Base64 encoded credentials in the form username:password>`

If the provided credentials are valid and your user account is enabled you will recieve a @ref Tgstation.Server.Api.Models.Token object
@code{.json}
{
    "bearer": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZXhwIjoiMTUzMzIzNjk0MSIsIm5iZiI6IjE1MzMyMzYwNDEiLCJpc3MiOiJUZ3N0YXRpb24uU2VydmVyLkhvc3QiLCJhdWQiOiJUZ3N0YXRpb24uU2VydmVyLkFwaSJ9.QWFSWNi9mgol582zaK4MQ5XDK2gZF-Nx3z9_ToHjKW4"
}
@endcode

If your account is disabled, you will receive a 403 response.

You may recognize the bearer value as a <a href="https://jwt.io">Json Web Token</a>. This is a secure representation of your identity to the server. It expires after a set period of time or until your password changes. It must be present for requests made to all other APIs. To do so add the following header to your other requests

- Authorization:Bearer `<your bearer token>`

Continue to use this token until you begin to receive 401 responses from the API. Then repeat the process to get a new one if your credentials are still valid

@subsection api_auth_o OAuth 2.0

TGS supports OAuth 2.0 with select providers for authentication.

The flow for this is as follows:

- Retrieve the @ref api_ver to find out which @ref Tgstation.Server.Api.Models.OAuthProvider are enabled and their respective information.
- Send the user to the Authorization Request endpoint for the provider using the client ID from above. See https://tools.ietf.org/html/rfc6749#section-4.1.1. DO NOT specify a redirect URI, this should be configured in the provider.
- Retrieve the authorization response code after successfully completing the authorize step above.
- Perform the following request:

POST "/" => @ref Tgstation.Server.Api.Models.Token

Headers:

- Authorization:OAuth `OAuth Authorization Response Code`
- OAuthProvider:<Provider Name>

You will be granted a bearer token as in basic auth. This will have an extended expiration to avoid repeating the entire process.

@subsubsection api_auth_o_providers Supported Providers

- GitHub: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps
- Discord: https://discord.com/developers/docs/topics/oauth2
- TGForums: https://tgstation13.org/phpBB/viewtopic.php?f=45&t=30155
- Keycloak: https://plugins.miniorange.com/keycloak-single-sign-on-wordpress-sso-oauth-openid-connect

@section api_perms Permissions

Almost all actions available require some level of user permissions. Which are divided into two categories:

- General permissions apply to a user across the entire service
- Instance permissions apply only when interacting with a specific instance

Permissions are represented a a 32 bit integer in the form of bitflags. The two endpoints for retrieving these permission are:

GET "/Users" => @ref Tgstation.Server.Api.Models.User
I GET "/InstanceUser" => @ref Tgstation.Server.Api.Models.InstanceUser

See individual documentation of each permission enum for their usage

@section api_transfer File Transfers

Certain responses inherit from @ref Tgstation.Server.Api.Models.FileTicketResult. These are special in that, having that model's @ref Tgstation.Server.Api.Models.FileTicketResult.FileTicket field populated indicates there is a pending file download or upload to take place. These are handled in the "/Transfer" endpoint.

To perform a file download make the following request:

GET "/Transfer?ticket=<@ref Tgstation.Server.Api.Models.FileTicketResult.FileTicket>" => application/octet-stream

To perform a file upload make the following request:

PUT "/Transfer?ticket=<@ref Tgstation.Server.Api.Models.FileTicketResult.FileTicket>" application/octet-stream => OK

File tickets are only valid for a short time after the initial request is made and should be dealt with immediately. Ensure that the file tickets are properly URL encoded.

@section api_user User Management

TGS start with one user: "Admin". The password is "ISolemlySwearToDeleteTheDataDirectory"

There are two types of users that can be created: Database users and system users. Database users are your plain login/password style users. System users are based on either Windows or POSIX accounts depending on the host system. They cannot be created by TGS and have to be activated by adding them to the Database similarly to a normal user using the "SystemIdentifier" field which should generally be set to their username or DOMAIN\\Username in an active directory environment. These act the same as regular users with the only exception being @ref api_config. See that section for details.

New users can be created with the following method.

PUT "/User" @ref Tgstation.Server.Api.Models.UserUpdate => @ref Tgstation.Server.Api.Models.User

To create a regular user, populate the name and password fields. To create a system user populate the SystemIdentifier field. Users rights and enabled status can also be specified in this call. To change them later use this API.

POST "/User" @ref Tgstation.Server.Api.Models.UserUpdate => @ref Tgstation.Server.Api.Models.User

Passowords for database users may be changed as well using this call. The capitalization of the display name of a user may also be changed

@subsection api_iuser Instance User Management

Instance users must be created for a given instance before being manipulated. The creator of an instance starts with full permissions on it

I PUT "/InstanceUser" @ref Tgstation.Server.Api.Models.InstanceUser => @ref Tgstation.Server.Api.Models.InstanceUser

They can be updated with a similar POST

I POST "/InstanceUser" @ref Tgstation.Server.Api.Models.InstanceUser => @ref Tgstation.Server.Api.Models.InstanceUser

Unlike regular users, they can also be deleted, which prevents the user from discovering the instance

I DELETE "/InstanceUser/{UserId}" => OK

Users with the permission to modify @ref Tgstation.Server.Api.Models.Instance objects can also gain user editing rights for any Instance. See @ref api_instance

@section api_admin Server-wide Administrative Actions

You can retrieve server update information with this

GET "/Administration" => @ref Tgstation.Server.Api.Models.Administration

If you want to perform a live update of the server use

POST "/Administration" @ref Tgstation.Server.Api.Models.Administration => OK

With the @ref Tgstation.Server.Api.Models.Administration.NewVersion field set

Any DreamDaemon servers running will persist while the server installs the new version from the official tgstation-server GitHub (If it exists)

If the server is otherwise acting funky and you wish to restart it, use this request:

DELETE "/Administration" => OK

@section api_inst Instances

Instances are DreamDaemon server configurations, they live in their own directory on the disk somewhere you specify. TGS starts with no default instance, to create one use this request:

PUT "/Instance" @ref Tgstation.Server.Api.Models.Instance => @ref Tgstation.Server.Api.Models.Instance

This normally returns 201 BUT, in the case of attaching an existing instance, 200 will instead be returned

The user that creates an instance will be given full @ref Tgstation.Server.Api.Models.InstanceUser permission. The path must not exist at the time of creation. Support for attaching instances from backups is yet to come.

A specific Instance may be retrieved with:

GET "/Instances/{InstanceID}" => @ref Tgstation.Server.Api.Models.Instance

Instances start offline, regardless of what was specified during the create request. An offline instance will return 403 for all requests made to it.

To online or change other instance variables use the following request. Note that using this request (even with an empty object) will automatically give you the @ref Tgstation.Server.Api.Rights.InstanceUserRights.WriteUsers right for that instance if you don't have it

POST "/Instance" @ref Tgstation.Server.Api.Models.Instance => @ref Tgstation.Server.Api.Models.Instance

Note that onlining an offline instance will never automatically start DreamDaemon. That must be done as a seperate step.

Instances can be detached which will delete all meta knowledge of the instance (Compile metadata, job metadata, repository commit metadata, Test merge metadata, etc...) but leave the files intact. That can be done with this request:

DELETE "/Instance/{InstanceId}" => OK

@subsection api_job Jobs

Some requests return @ref Tgstation.Server.Api.Models.Job objects. These are long running tasks the server will perform asyncronously and can be polled for status.

Note that the job representing instance move operations must be queried and cancelled differently than others. See the documentation of @ref Tgstation.Server.Api.Models.Instance.MoveJob for details

To list all jobs in an Instance use the following request

I GET "/Job/List" => Array of @ref Tgstation.Server.Api.Models.Job

Note that the response for this request will only have the @ref Tgstation.Server.Api.Models.Job.Id field populated

To get full details of _active_ jobs use the following request:

I GET "/Job" => Array of @ref Tgstation.Server.Api.Models.Job

To get full details of a specific job use the following request:

I GET "/Job/{JobId}" => @ref Tgstation.Server.Api.Models.Job

To cancel a running job (If the job can be cancelled and you have sufficient rights) use the following request:

I DELETE "/Job/{JobId}" => OK

@subsection api_chat Chat Bots

Each chat bot is represented by a @ref Tgstation.Server.Api.Models.ChatBot object

Chat bots can be created/updated/deleted with the following requests respectively

I PUT "/Chat" @ref Tgstation.Server.Api.Models.ChatBot => Tgstation.Server.Api.Models.ChatBot
I POST "/Chat" @ref Tgstation.Server.Api.Models.ChatBot => Tgstation.Server.Api.Models.ChatBot
I DELETE "/Chat/{ChatBotId}" => OK

The @ref Tgstation.Server.Api.Models.Internal.ChatBot.ConnectionString must differ based on what kind of chat bot you wish to create. Each @ref Tgstation.Server.Api.Models.ChatProvider has a @ref Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder that dictates how to form it

For IRC chat bots see @ref Tgstation.Server.Api.Models.IrcConnectionStringBuilder
For Discord chat bots see @ref Tgstation.Server.Api.Models.DiscordConnectionStringBuilder

A specific bot's settings may be retrieved with:

I GET "/Chat/{ChatBotId}" => @ref Tgstation.Server.Api.Models.ChatBot

Also note that if the @ref Tgstation.Server.Api.Models.ChatBot.Channels is present in a POST request, the list will fully replace any active channels

@subsection api_byond Byond Version Management

To get the Byond version used for new compilations use the following request:

I GET "/Byond" => @ref Tgstation.Server.Api.Models.Byond

To set the active Byond version:

I POST "/Byond" @ref Tgstation.Server.Api.Models.Byond => @ref Tgstation.Server.Api.Models.Byond

This will queue up a job to install the byond version if it doesn't exist which will be returned in the response. The @ref Tgstation.Server.Api.Models.Internal.RawData.Content field can be used to upload custom zip files.

To list all installed Byond versions use the following request:

I GET "/Byond/List" => Array of @ref Tgstation.Server.Api.Models.Byond

@subsection Git Repository Management

To read the current repository state use the following request:

I GET "/Repository" => @ref Tgstation.Server.Api.Models.Repository

To clone the repository if it doesn't yet exist use the following request:

I PUT "/Repository" => @ref Tgstation.Server.Api.Models.Repository

The clone job will be represented by the @ref Tgstation.Server.Api.Models.Repository.ActiveJob field. Specify the @ref Tgstation.Server.Api.Models.Repository.Origin URL. Optionally specify the initial @ref Tgstation.Server.Api.Models.Repository.Reference as a git tag or branch. Be sure to specify the authentication fields if necessary to access your repository. Will return 409 if the repository already exists or is already being cloned

To delete an existing repository make the following request:

I DELETE "/Repository" => OK

Modifications to the repository are done with the following request:

I POST "/Repository" => @ref Tgstation.Server.Api.Models.Repository => @ref Tgstation.Server.Api.Models.Repository

Each update that requires git changes creates a job specified in the @ref Tgstation.Server.Api.Models.Repository.ActiveJob field jobs will be queued in succession. See below for POST examples.

@subsubsection api_repopost Repository Commands for Git Aliases

All these actions are done with the POST method

git pull (Only if @ref Tgstation.Server.Api.Models.Repository.Reference is set):
@code{.json}
{
	"updateFromOrigin": true
}
@endcode

git checkout <`commit sha`> (Unsets @ref Tgstation.Server.Api.Models.Repository.Reference):
@code{.json}
{
	"checkoutSha": "<commit sha>"
}
@endcode

git checkout -f <`branch or tag`> && git clean -fxd (Sets @ref Tgstation.Server.Api.Models.Repository.Reference):
@code{.json}
{
	"reference": "<branch or tag>"
}
@endcode

git fetch && git checkout -f branch && git clean -fxd && git reset --hard origin/branch (Sets @ref Tgstation.Server.Api.Models.Repository.Reference);
@code{.json}
{
	"updateFromOrigin": true,
	"reference": "branch"
}
@endcode

@subsubsection api_repotm Test Merging

Any of the above POST methods can be combined with the @ref Tgstation.Server.Api.Models.Repository.NewTestMerges field to add test merges on top of the checked out commit.

@code{.json}
{
	"newTestMerges": [
		{
			"number": 12345,
			"comment": "I'm merging this for x reason"
		},
		{
			"number": 12346,
			"pullRequestRevision": "abcdef1"
		}
	]
}
@endcode

If the server detects a set of @ref Tgstation.Server.Api.Models.TestMergeParameters being applied that it has seen before, it'll instead attempt to checkout the commit that was created then.

@subsubsection api_repounsetauth Unsetting Authentication

The repository uses the @ref Tgstation.Server.Api.Models.Repository.AccessUser and @ref Tgstation.Server.Api.Models.Repository.AccessToken credentials to access the remote repository if these fields are set. To unset them you must set both of them to an empty string like so

@code{.json}
{
	"accessUser": "",
	"accessToken": ""
}
@endcode

This will remove both fields from the database

@subsection api_dm Compiler Handling

The DM compiler can run one job at a time for an Instance. Instead of using standard @ref Tgstation.Server.Api.Models.Job.Progress it uses the @ref Tgstation.Server.Api.Models.DreamMaker.Status field to denote it's current stage. It can be retrieved like this:

I GET "/DreamMaker" => @ref Tgstation.Server.Api.Models.DreamMaker

This will also return the last successful @ref Tgstation.Server.Api.Models.CompileJob as well as the @ref Tgstation.Server.Api.Models.Internal.DreamMakerSettings.ProjectName

The ProjectName field can be changed with this method:

I POST "/DreamMaker" @ref Tgstation.Server.Api.Models.DreamMaker => @ref Tgstation.Server.Api.Models.DreamMaker

To start a compile job use the following request:

I PUT "/DreamMaker" Empty => @ref Tgstation.Server.Api.Models.Job

The job object returned represents the compile job

"Empty" refers to an empty json object like so:

@code{.json}
{}
@endcode

The compiler endpoint is also used to read compile jobs

To list successful compile jobs ids use:

I GET "/DreamMaker/List" => Array of @ref Tgstation.Server.Api.Models.CompileJob

To get a specific compile job use:

I GET "/DreamMaker/{CompileJobId}" => @ref Tgstation.Server.Api.Models.CompileJob

@subsection api_config Static Files

Static files can be both read and written.

To get the contents of a static file directory use the following method

I GET "/Config/List/<path to directory>" => Array of @ref Tgstation.Server.Api.Models.ConfigurationFile

Where the path is formatted as: directory/inner_directory/more_directories

If the path is empty, the root directory will be retrieved. The @ref Tgstation.Server.Api.Models.ConfigurationFile.Content and @ref Tgstation.Server.Api.Models.ConfigurationFile.LastReadHash fields will not be populated for this response

If you do not have access to list the requested directory, a 403 response will be returned. If the path actually represents a file or the directory no longer exists a 410 response will be returned.

To create an empty config directory use the following request

I PUT "/Config" @ref Tgstation.Server.Api.Models.ConfigurationFile => @ref Tgstation.Server.Api.Models.ConfigurationFile

To get the content of a static file use the following method

I GET "/Config/File/<path to file>" => @ref Tgstation.Server.Api.Models.ConfigurationFile

410 will be returned if the path doesn't exist. @ref Tgstation.Server.Api.Models.ConfigurationFile.Content will only be populated if the path is a file the user has access to, otherwise the other fields will be populated appropriately.

To create, write, and delete files use the following request

I POST "/Config" @ref Tgstation.Server.Api.Models.ConfigurationFile => @ref Tgstation.Server.Api.Models.ConfigurationFile

When creating a file, only @ref Tgstation.Server.Api.Models.ConfigurationFile.Path and @ref Tgstation.Server.Api.Models.ConfigurationFile.Content should be specified

If the file already exists, the @ref Tgstation.Server.Api.Models.ConfigurationFile.LastReadHash field must also be present with the last version received from the server for that file. If this does not match at the time of the request, 409 will be returned, indicating the file has changed since it was last viewed by the client.

To delete a file set @ref Tgstation.Server.Api.Models.ConfigurationFile.Content to null in the request.


To delete an empty directory use the following request

I DELETE "/Config" @ref Tgstation.Server.Api.Models.ConfigurationFile => OK

Where the request @ref Tgstation.Server.Api.Models.ConfigurationFile.Path is set to the directory to delete


@subsection api_dog Watchdog

To read watchdog status use the following request:

I GET "/DreamDaemon" => @ref Tgstation.Server.Api.Models.DreamDaemon

To update watchdog settings use the following request:

I POST "/DreamDaemon" => @ref Tgstation.Server.Api.Models.DreamDaemon

Note that soft restart/shutdown operations cannot be cancelled via this method, they can be changed from one to another however.

To start the watchdog use the following request:

I PUT "/DreamDaemon" Empty => @ref Tgstation.Server.Api.Models.Job

To restart the watchdog use the following request:

I PATCH "/DreamDaemon" Empty => @ref Tgstation.Server.Api.Models.Job

The returned jobs represents the startup process for the watchdog

To stop the watchdog use the following request:

I DELETE "/DreamDaemon" => OK

*/
