-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #182 from southworks/dev
[Feature] Multi-language support and automated PR creation for Application/Game contribution
- Loading branch information
Showing
76 changed files
with
5,205 additions
and
274 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { DEFAULT_LOCALE, LOCALES } from "./src/config/i18nConfig.js"; | ||
|
||
export default { | ||
defaultLocale: DEFAULT_LOCALE, | ||
locales: LOCALES, | ||
load: ["server", "client"], | ||
resourcesBasePath: "/locales", | ||
showDefaultLocale: true, | ||
i18nextServer: { | ||
debug: true, | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
GITHUB_TOKEN=<your-github-pat-token> | ||
GITHUB_OWNER=<owner> | ||
GITHUB_REPO=<repo> | ||
GITHUB_BASE_BRANCH=<base_branch> | ||
GITHUB_APP_ID=<github_app_id> | ||
GITHUB_APP_INSTALLATION_ID=<github_app_id> | ||
GITHUB_APP_PK_BASE64="<base-64-encoded-github-app-private-key" | ||
RECAPTCHA_V2_VERIFY_URL="https://www.google.com/recaptcha/api/siteverify" | ||
RECAPTCHA_V2_SECRET_KEY="<recaptcha-v2-secret-key>" | ||
SENDGRID_API_KEY="<sendgird-api-key>" | ||
SENDER_EMAIL="<sender-email>" | ||
RECIPIENT_EMAIL="<recipient-email>" | ||
ALLOWED_REFERER="<allowed-referer>" | ||
SECRET_NAME="<secret_name>" | ||
AWS_SECRET_REGION="<aws_region>" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import RequestValidator from './utils/request-validator.js'; | ||
import EmailManager from './utils/email-manager.js'; | ||
import ConfigManager from './utils/config-manager.js'; | ||
import PullRequestManager from './utils/pull-request-manager.js'; | ||
import IssueManager from './utils/issue-manager.js'; | ||
import { FileType } from "./models/file-type.js"; | ||
|
||
const configManager = new ConfigManager(); | ||
let config; | ||
|
||
const HEADERS = { | ||
'Access-Control-Allow-Origin': '*', | ||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', | ||
'Access-Control-Allow-Headers': 'Content-Type', | ||
}; | ||
|
||
const RequestTypes = Object.freeze({ | ||
RequestThisAppToBeTested: "requestThisAppToBeTested", | ||
SubmitNewOrUpdatedAppInfo: "submitNewOrUpdatedAppInfo" | ||
}); | ||
|
||
export const handler = async (event) => { | ||
console.log('Received event:', JSON.stringify(event, null, 2)); | ||
|
||
try { | ||
const formData = JSON.parse(event.body); | ||
|
||
const refererError = RequestValidator.validateReferer(event.headers, process.env['ALLOWED_REFERER']); | ||
if(!refererError) config = await configManager.loadConfig(FileType.Application); | ||
const validationError = refererError === "" ? await RequestValidator.validateRequest(formData, FileType.Application, config.recaptchaV2VerifyUrl, config.recaptchaV2SecretKey) : 'Access forbidden'; | ||
if (validationError) { | ||
return { statusCode: validationError === 'Access forbidden' ? 403 : 400, headers: HEADERS, body: validationError }; | ||
} | ||
|
||
switch (formData.requestType) { | ||
case RequestTypes.RequestThisAppToBeTested: | ||
await handleTestingRequest(formData); | ||
break; | ||
case RequestTypes.SubmitNewOrUpdatedAppInfo: | ||
await handleSubmitOrUpdateRequest(formData); | ||
break; | ||
default: | ||
return { statusCode: 400, headers: HEADERS, body: `Invalid request type: ${formData.requestType}` }; | ||
} | ||
|
||
return { statusCode: 200, headers: HEADERS, body: 'Successfully processed request' }; | ||
} catch (error) { | ||
return handleError(error); | ||
} | ||
}; | ||
|
||
const handleTestingRequest = async (data) => { | ||
const issueManager = new IssueManager(); | ||
console.log(`Creating Issue for ${data.name} from ${data.publisher}.`); | ||
await issueManager.createIssue(data, { | ||
githubAppId: config.githubAppId, | ||
githubAppInstallationId: config.githubAppInstallationId, | ||
githubAppPkBase64: config.githubAppPkBase64, | ||
githubOwner: config.githubOwner, | ||
githubRepo: config.githubRepo | ||
}); | ||
console.log(`Issue for ${data.name} from ${data.publisher} successfully created.`); | ||
}; | ||
|
||
const handleSubmitOrUpdateRequest = async (data) => { | ||
const pullRequestManager = new PullRequestManager(); | ||
await pullRequestManager.createPullRequest(data, { | ||
githubAppId: config.githubAppId, | ||
githubAppInstallationId: config.githubAppInstallationId, | ||
githubAppPkBase64: config.githubAppPkBase64, | ||
githubOwner: config.githubOwner, | ||
githubRepo: config.githubRepo, | ||
githubBaseBranch: config.githubBaseBranch | ||
}, FileType.Application); | ||
}; | ||
|
||
const handleError = (error) => { | ||
console.error(error); | ||
return { statusCode: 500, headers: HEADERS, body: "An error occurred while processing your form submission." }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import RequestValidator from './utils/request-validator.js'; | ||
import ConfigManager from './utils/config-manager.js'; | ||
import PullRequestManager from './utils/pull-request-manager.js'; | ||
import { FileType } from "./models/file-type.js"; | ||
|
||
const configManager = new ConfigManager(); | ||
let config; | ||
|
||
const HEADERS = { | ||
'Access-Control-Allow-Origin': '*', | ||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', | ||
'Access-Control-Allow-Headers': 'Content-Type', | ||
}; | ||
|
||
export const handler = async (event) => { | ||
console.log('Received event:', JSON.stringify(event, null, 2)); | ||
|
||
try { | ||
const formData = JSON.parse(event.body); | ||
|
||
const refererError = RequestValidator.validateReferer(event.headers, process.env['ALLOWED_REFERER']); | ||
if(!refererError) config = await configManager.loadConfig(FileType.Game); | ||
const validationError = refererError === "" ? await RequestValidator.validateRequest(formData, FileType.Game, config.recaptchaV2VerifyUrl, config.recaptchaV2SecretKey) : 'Access forbidden'; | ||
if (validationError) { | ||
return { statusCode: validationError === 'Access forbidden' ? 403 : 400, headers: HEADERS, body: validationError }; | ||
} | ||
|
||
const pullRequestManager = new PullRequestManager(); | ||
|
||
await pullRequestManager.createPullRequest( | ||
formData, | ||
{ | ||
githubAppId: config.githubAppId, | ||
githubAppInstallationId: config.githubAppInstallationId, | ||
githubAppPkBase64: config.githubAppPkBase64, | ||
githubOwner: config.githubOwner, | ||
githubRepo: config.githubRepo, | ||
githubBaseBranch: config.githubBaseBranch | ||
}, | ||
FileType.Game | ||
); | ||
return { statusCode: 200, headers: HEADERS, body: 'Successfully processed request' }; | ||
} catch (error) { | ||
return handleError(error); | ||
} | ||
}; | ||
|
||
const handleError = (error) => { | ||
console.error(error); | ||
return { statusCode: 500, headers: HEADERS, body: "An error occurred while processing your form submission." }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const FileType = Object.freeze({ | ||
Application: "Application", | ||
Game: "Game" | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
export const pullRequestTemplateData = { | ||
applicationFileContentTemplate: template`---\nname: ${0}\ncategories: [${1}]\ncompatibility: ${2}\ndisplay_result: ${3}\nversion_from: ${4}\nlink: ${5}\nicon: ${6}\n---`, | ||
applicationFilePathTemplate: template`src/content/applications/${0}.md`, | ||
commitMessageTemplate: template`Add ${0}.md file`, | ||
gameFileContentTemplate: template`---\nname: ${0}\ncategories: [${1}]\npublisher: ${2}\ncompatibility: ${3}\ncompatibility_details: ${4}\ndevice_configuration: ${5}\ndate_tested: ${6}\nos_version: ${7}\ndriver_id: ${8}${9}\n---`, | ||
gameFileSubContentTemplate: template`\nauto_super_resolution:\n\tcompatibility: ${0}\n\tfps boost: ${1}`, | ||
gameFilePathTemplate: template`src/content/games/${0}.md`, | ||
headBaseBranchTemplate: template`heads/${0}`, | ||
newBranchNameTemplate: template`feat/add-${0}-${1}-file-${2}`, // ex: feat/add-outlook-application-file-1234 | ||
newBranchRefNameTemplate: template`refs/heads/${0}`, | ||
pullRequestBodyTemplate: template`Incoming changes:\n- Add file for ${0} ${1} from ${2}\n- This data corresponds to a new ${1}\n\n**NOTE**: this Pull Request has been automatically generated.`, | ||
pullRequestTitleTemplate: template`[Feature] Add file for ${0} ${1}`, // ex: [Feature] Add file for Outlook Application | ||
}; | ||
|
||
export const issueTemplateData = { | ||
title: template`[Request] Test ${0} application`, | ||
body: template`This issue corresponds to a request to test the **${0}** application from **${1}**.\nPlease proceed with the testing process at your earliest convenience.\n**NOTE**: this Issue has been automatically generated.` | ||
} | ||
|
||
// Tagged template function for creating templates with placeholders | ||
// Usage: const value = template`list of values: ${0}, ${1}, {2}`; | ||
// value("first", "second", "third"); // outputs: "list of values: first, second, third"; | ||
function template(strings, ...keys) { | ||
return (...values) => { | ||
const dict = values[values.length - 1] || {}; | ||
const result = [strings[0]]; | ||
keys.forEach((key, i) => { | ||
const value = Number.isInteger(key) ? values[key] : dict[key]; | ||
result.push(value, strings[i + 1]); | ||
}); | ||
return result.join(""); | ||
}; | ||
} |
Oops, something went wrong.