Page 1 of 1

[Released] Mod Upload API

Posted: Fri Mar 04, 2022 1:37 pm
by vinzenz
EDIT:
The Mod upload API is now released and properly documented on the Factorio wiki: https://wiki.factorio.com/Mod_upload_API
Testing the new Experimental Mod Upload API
I'm happy to announce that our new mod portal upload API is ready for public testing! Please tell me what you think about it.

It's still in experimental state, so please don't update all your tools and clients just yet. There will be changes, depending on your feedback and stabilty of the process.

The Mod Upload API only supports adding releases to existing mods. Support for publishing mods and editing mod details is planned.


Authentication
To authenticate requests to this new mod portal API you need to create a special API Key on your factorio.com/profile page:
Pasted image 20220304095807.png
Pasted image 20220304095807.png (11.18 KiB) Viewed 2734 times
Endpoints
init_upload
URL: https://mods.factorio.com/api/v2/mods/r ... nit_upload

HTTP Method
POST

HTTP Request Headers
Authorization:: Authorization header with the APIKey and a "Bearer " prefix -> "Bearer $APIKey"

HTTP Request Body
mod: Name of the mod, for which a new release should be uploaded

JSON object response
upload_url::string: URL the mod zip file should be uploaded to

upload
URL: $upload_url, URL returned by the init_upload endpoint

HTTP Method
POST

HTTP Request Body
file: Mod zip file

JSON object response
success :: bool: This attribute only appears on successful uploads. It's set to true.
error:: string: This attribute only appears on failed uploads. Can contain details about the problem
message :: string: This attribute can appear on failed uploads. Can contains details about the problem

Python Example

Code: Select all

import sys
import requests
from os import getenv

MOD_PORTAL_URL = "https://mods.factorio.com"
INIT_UPLOAD_URL = f"{MOD_PORTAL_URL}/api/v2/mods/releases/init_upload"

apikey = getenv("MOD_UPLOAD_API_KEY")
modname = getenv("MOD_UPLOAD_NAME")
zipfilepath = getenv("MOD_UPLOAD_FILE")

request_body = data={"mod":modname}
request_headers = {"Authorization": f"Bearer {apikey}"}

response = requests.post(
	INIT_UPLOAD_URL,
	data=request_body,
	headers=request_headers)

if not response.ok:	
	print(f"init_upload failed: {response.text}")
	sys.exit(1)

upload_url = response.json()["upload_url"]

with open(zipfilepath, "rb") as f:	
	request_body = {"file": f}	
	response = requests.post(upload_url, files=request_body)

if not response.ok:	
	print(f"upload failed: {response.text}")	
	sys.exit(1)

print(f"upload successful: {response.text}")
Planned Changes
Released Changes
  • docs: fix name of Authorization Header (was documented as Authentication, was in fact called Authorization)
  • unify responses of upload endpoint
  • change endpoint url to v2 prefix
  • document possible error values
  • create wiki page

Technical Details
The new upload API is built on a refactored version of the old mod uploader and is hosted on heroku instead of a cloud vm. First the mod portal generates a temporary url for the mod uploader, then the mod uploader takes your zip file, extracts information, uploads it to the CDN, forwards the info to the mod portal and then responds with the mod portal result.

Re: Testing: Experimental Mod Upload API

Posted: Sun Mar 13, 2022 5:36 pm
by HellLaw
Hello Vinzenz,

I had noticed the changes you have made on the Wiki recently (including https://wiki.factorio.com/Factorio_HTTP ... guidelines).
I was already enthusiastic about it so I'm glad to see that this is ongoing work !

I have personally been using the Mod portal API for a side project.
I knew that it was an "unsupported" API: it is now very clear what quality level we, users, should expect.
Thank you for that !

I have never experimented with the old mod uploader but I will eventually try the new one (hopefully "soon", you never know with side projects :D).
That would be to update one of Factorio GitHub actions or create my own.

As of now, my feedbacks would be:
  • It is great that the authentication is know using an API key instead of the username/token pair previously used.
  • Looks weird to have two endpoints for a single operation (I am no HTTP API expert though, maybe I am missing something here).
Also, any roadmap for other HTTP APIs would be appreciated ;)
And I would be happy to help if you need any input.

Have a good day !
HellLaw

Re: Testing: Experimental Mod Upload API

Posted: Mon Mar 14, 2022 10:41 am
by vinzenz
HellLaw wrote:
Sun Mar 13, 2022 5:36 pm
As of now, my feedbacks would be:
  • It is great that the authentication is know using an API key instead of the username/token pair previously used.
  • Looks weird to have two endpoints for a single operation (I am no HTTP API expert though, maybe I am missing something here).
Also, any roadmap for other HTTP APIs would be appreciated ;)
And I would be happy to help if you need any input.
Thanks for the great feedback!

The second endpoint is run by a separate web service mainly to ensure uploads can't impact the main mod portal service. Also unpacking untrusted zip files in a process that doesn't have access to database credentials feels better to me :D

For the API roadmap I would like to finish the basic mod interactions first, so editing mod details, publishing mods, uploading mod images will come next. Maybe not in that particular order. But I also want to work with the community to determine priorities.

Re: [Released] Mod Upload API

Posted: Thu Jan 19, 2023 2:03 pm
by xzar90
I I would like help with this, I get this error:

I don't understand which payload format it accept.

Code: Select all

            
            var requestUrl = $"{_moddingOption.ModPortalUrl}/api/v2/mods/releases/init_upload";
            var result = await _httpClient.PostAsJsonAsync(requestUrl, new { mod = info.Name });
            if (result.IsSuccessStatusCode)
            ....
{"error":"InvalidRequest","message":"{'mod': ['Missing data for required field.']}"}
I also tried string content "data={"mod": "nameofmod"}"

Re: [Released] Mod Upload API

Posted: Thu Jan 19, 2023 2:11 pm
by vinzenz
xzar90 wrote:
Thu Jan 19, 2023 2:03 pm
I don't understand which payload format it accept.
It's expecting application/x-www-form-urlencoded data, so it will return an error for json. I think in C# you need to use FormUrlEncodedContent.

Re: [Released] Mod Upload API

Posted: Thu Jan 19, 2023 2:17 pm
by xzar90
Thank you it is working.

Code: Select all

        public async Task<bool> PostInitAsync(ModInfo info, string zipFilePath)
        {
            var requestUrl = $"{_moddingOption.ModPortalUrl}/api/v2/mods/releases/init_upload";
            var result = await _httpClient.PostAsync(requestUrl, new FormUrlEncodedContent(
                new Dictionary<string,string>(1) { { "mod", info.Name } }));
            if (result.IsSuccessStatusCode)
            {
                var initResponse = await result.Content.ReadFromJsonAsync<InitModPortalResponse>();
                if (await PostUploadAsync(zipFilePath, initResponse!))
                {
                    return true;
                }

                return false;
            }

            await LogErrorAsync(result, nameof(PostInitAsync));
            return false;
        }

        private async Task LogErrorAsync(HttpResponseMessage result, string type)
        {
            var errorResponse = await result.Content.ReadAsStringAsync();
            _logger.LogError(
                "{StatusCode}: Something went wrong with {Type}: {errorResponse}",
                result.StatusCode,
                type,
                errorResponse);
        }