mirror of
https://github.com/dscalzi/HeliosLauncher.git
synced 2024-12-22 19:52:14 -08:00
Compare commits
5 Commits
f119985211
...
d560ef09d1
Author | SHA1 | Date | |
---|---|---|---|
|
d560ef09d1 | ||
|
3a36ce7094 | ||
|
ad47617cd0 | ||
|
2fdb217e64 | ||
|
c1d36d2b03 |
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@ -14,16 +14,22 @@ jobs:
|
|||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
- name: Install Node.js, NPM and Yarn
|
- name: Set up Node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
|
|
||||||
- name: Build/release Electron app
|
- name: Set up Python
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.github_token }}
|
python-version: 3.x
|
||||||
|
|
||||||
# If the commit is tagged with a version (e.g. "v1.0.0"),
|
- name: Install Dependencies
|
||||||
# release the app after building
|
run: npm ci
|
||||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
shell: bash
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.github_token }}
|
||||||
|
run: npm run dist
|
||||||
|
shell: bash
|
@ -16,13 +16,6 @@ const ConfigManager = require('./configmanager')
|
|||||||
const DistroManager = require('./distromanager')
|
const DistroManager = require('./distromanager')
|
||||||
const isDev = require('./isdev')
|
const isDev = require('./isdev')
|
||||||
|
|
||||||
// Constants
|
|
||||||
// const PLATFORM_MAP = {
|
|
||||||
// win32: '-windows-x64.tar.gz',
|
|
||||||
// darwin: '-macosx-x64.tar.gz',
|
|
||||||
// linux: '-linux-x64.tar.gz'
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
|
|
||||||
/** Class representing a base asset. */
|
/** Class representing a base asset. */
|
||||||
|
@ -10,10 +10,11 @@
|
|||||||
*/
|
*/
|
||||||
// Requirements
|
// Requirements
|
||||||
const ConfigManager = require('./configmanager')
|
const ConfigManager = require('./configmanager')
|
||||||
const LoggerUtil = require('./loggerutil')
|
const { LoggerUtil } = require('helios-core')
|
||||||
const Mojang = require('./mojang')
|
const { MojangRestAPI, mojangErrorDisplayable, MojangErrorCode } = require('helios-core/mojang')
|
||||||
const logger = LoggerUtil('%c[AuthManager]', 'color: #a02d2a; font-weight: bold')
|
const { RestResponseStatus } = require('helios-core/common')
|
||||||
const loggerSuccess = LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight: bold')
|
|
||||||
|
const log = LoggerUtil.getLogger('AuthManager')
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
@ -28,7 +29,11 @@ const loggerSuccess = LoggerUtil('%c[AuthManager]', 'color: #209b07; font-weight
|
|||||||
*/
|
*/
|
||||||
exports.addAccount = async function(username, password){
|
exports.addAccount = async function(username, password){
|
||||||
try {
|
try {
|
||||||
const session = await Mojang.authenticate(username, password, ConfigManager.getClientToken())
|
const response = await MojangRestAPI.authenticate(username, password, ConfigManager.getClientToken())
|
||||||
|
console.log(response)
|
||||||
|
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||||
|
|
||||||
|
const session = response.data
|
||||||
if(session.selectedProfile != null){
|
if(session.selectedProfile != null){
|
||||||
const ret = ConfigManager.addAuthAccount(session.selectedProfile.id, session.accessToken, username, session.selectedProfile.name)
|
const ret = ConfigManager.addAuthAccount(session.selectedProfile.id, session.accessToken, username, session.selectedProfile.name)
|
||||||
if(ConfigManager.getClientToken() == null){
|
if(ConfigManager.getClientToken() == null){
|
||||||
@ -37,11 +42,16 @@ exports.addAccount = async function(username, password){
|
|||||||
ConfigManager.save()
|
ConfigManager.save()
|
||||||
return ret
|
return ret
|
||||||
} else {
|
} else {
|
||||||
throw new Error('NotPaidAccount')
|
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.ERROR_NOT_PAID))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return Promise.reject(mojangErrorDisplayable(response.mojangErrorCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (err){
|
} catch (err){
|
||||||
return Promise.reject(err)
|
log.error(err)
|
||||||
|
return Promise.reject(mojangErrorDisplayable(MojangErrorCode.UNKNOWN))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,11 +65,17 @@ exports.addAccount = async function(username, password){
|
|||||||
exports.removeAccount = async function(uuid){
|
exports.removeAccount = async function(uuid){
|
||||||
try {
|
try {
|
||||||
const authAcc = ConfigManager.getAuthAccount(uuid)
|
const authAcc = ConfigManager.getAuthAccount(uuid)
|
||||||
await Mojang.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
|
const response = await MojangRestAPI.invalidate(authAcc.accessToken, ConfigManager.getClientToken())
|
||||||
|
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||||
ConfigManager.removeAuthAccount(uuid)
|
ConfigManager.removeAuthAccount(uuid)
|
||||||
ConfigManager.save()
|
ConfigManager.save()
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
} else {
|
||||||
|
log.error('Error while removing account', response.error)
|
||||||
|
return Promise.reject(response.error)
|
||||||
|
}
|
||||||
} catch (err){
|
} catch (err){
|
||||||
|
log.error('Error while removing account', err)
|
||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,24 +92,27 @@ exports.removeAccount = async function(uuid){
|
|||||||
*/
|
*/
|
||||||
exports.validateSelected = async function(){
|
exports.validateSelected = async function(){
|
||||||
const current = ConfigManager.getSelectedAccount()
|
const current = ConfigManager.getSelectedAccount()
|
||||||
const isValid = await Mojang.validate(current.accessToken, ConfigManager.getClientToken())
|
const response = await MojangRestAPI.validate(current.accessToken, ConfigManager.getClientToken())
|
||||||
|
|
||||||
|
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||||
|
const isValid = response.data
|
||||||
if(!isValid){
|
if(!isValid){
|
||||||
try {
|
const refreshResponse = await MojangRestAPI.refresh(current.accessToken, ConfigManager.getClientToken())
|
||||||
const session = await Mojang.refresh(current.accessToken, ConfigManager.getClientToken())
|
if(refreshResponse.responseStatus === RestResponseStatus.SUCCESS) {
|
||||||
|
const session = refreshResponse.data
|
||||||
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
|
ConfigManager.updateAuthAccount(current.uuid, session.accessToken)
|
||||||
ConfigManager.save()
|
ConfigManager.save()
|
||||||
} catch(err) {
|
} else {
|
||||||
logger.debug('Error while validating selected profile:', err)
|
log.error('Error while validating selected profile:', refreshResponse.error)
|
||||||
if(err && err.error === 'ForbiddenOperationException'){
|
log.info('Account access token is invalid.')
|
||||||
// What do we do?
|
|
||||||
}
|
|
||||||
logger.log('Account access token is invalid.')
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
loggerSuccess.log('Account access token validated.')
|
log.info('Account access token validated.')
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
loggerSuccess.log('Account access token validated.')
|
log.info('Account access token validated.')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
// Work in progress
|
// Work in progress
|
||||||
const logger = require('./loggerutil')('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
|
const logger = require('./loggerutil')('%c[DiscordWrapper]', 'color: #7289da; font-weight: bold')
|
||||||
|
|
||||||
const {Client} = require('discord-rpc')
|
const {Client} = require('discord-rpc-patch')
|
||||||
|
|
||||||
let client
|
let client
|
||||||
let activity
|
let activity
|
||||||
|
@ -1,271 +0,0 @@
|
|||||||
/**
|
|
||||||
* Mojang
|
|
||||||
*
|
|
||||||
* This module serves as a minimal wrapper for Mojang's REST api.
|
|
||||||
*
|
|
||||||
* @module mojang
|
|
||||||
*/
|
|
||||||
// Requirements
|
|
||||||
const request = require('request')
|
|
||||||
const logger = require('./loggerutil')('%c[Mojang]', 'color: #a02d2a; font-weight: bold')
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const minecraftAgent = {
|
|
||||||
name: 'Minecraft',
|
|
||||||
version: 1
|
|
||||||
}
|
|
||||||
const authpath = 'https://authserver.mojang.com'
|
|
||||||
const statuses = [
|
|
||||||
{
|
|
||||||
service: 'session.minecraft.net',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Multiplayer Session Service',
|
|
||||||
essential: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'authserver.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Authentication Service',
|
|
||||||
essential: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'textures.minecraft.net',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Minecraft Skins',
|
|
||||||
essential: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'api.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Public API',
|
|
||||||
essential: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'minecraft.net',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Minecraft.net',
|
|
||||||
essential: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
service: 'account.mojang.com',
|
|
||||||
status: 'grey',
|
|
||||||
name: 'Mojang Accounts Website',
|
|
||||||
essential: false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a Mojang status color to a hex value. Valid statuses
|
|
||||||
* are 'green', 'yellow', 'red', and 'grey'. Grey is a custom status
|
|
||||||
* to our project which represents an unknown status.
|
|
||||||
*
|
|
||||||
* @param {string} status A valid status code.
|
|
||||||
* @returns {string} The hex color of the status code.
|
|
||||||
*/
|
|
||||||
exports.statusToHex = function(status){
|
|
||||||
switch(status.toLowerCase()){
|
|
||||||
case 'green':
|
|
||||||
return '#a5c325'
|
|
||||||
case 'yellow':
|
|
||||||
return '#eac918'
|
|
||||||
case 'red':
|
|
||||||
return '#c32625'
|
|
||||||
case 'grey':
|
|
||||||
default:
|
|
||||||
return '#848484'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the status of Mojang's services.
|
|
||||||
* The response is condensed into a single object. Each service is
|
|
||||||
* a key, where the value is an object containing a status and name
|
|
||||||
* property.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Mojang_API#API_Status
|
|
||||||
*/
|
|
||||||
exports.status = function(){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.get('https://status.mojang.com/check',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
timeout: 2500
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
|
|
||||||
if(error || response.statusCode !== 200){
|
|
||||||
logger.warn('Unable to retrieve Mojang status.')
|
|
||||||
logger.debug('Error while retrieving Mojang statuses:', error)
|
|
||||||
//reject(error || response.statusCode)
|
|
||||||
for(let i=0; i<statuses.length; i++){
|
|
||||||
statuses[i].status = 'grey'
|
|
||||||
}
|
|
||||||
resolve(statuses)
|
|
||||||
} else {
|
|
||||||
for(let i=0; i<body.length; i++){
|
|
||||||
const key = Object.keys(body[i])[0]
|
|
||||||
inner:
|
|
||||||
for(let j=0; j<statuses.length; j++){
|
|
||||||
if(statuses[j].service === key) {
|
|
||||||
statuses[j].status = body[i][key]
|
|
||||||
break inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(statuses)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticate a user with their Mojang credentials.
|
|
||||||
*
|
|
||||||
* @param {string} username The user's username, this is often an email.
|
|
||||||
* @param {string} password The user's password.
|
|
||||||
* @param {string} clientToken The launcher's Client Token.
|
|
||||||
* @param {boolean} requestUser Optional. Adds user object to the reponse.
|
|
||||||
* @param {Object} agent Optional. Provided by default. Adds user info to the response.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Authenticate
|
|
||||||
*/
|
|
||||||
exports.authenticate = function(username, password, clientToken, requestUser = true, agent = minecraftAgent){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
agent,
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
requestUser
|
|
||||||
}
|
|
||||||
if(clientToken != null){
|
|
||||||
body.clientToken = clientToken
|
|
||||||
}
|
|
||||||
|
|
||||||
request.post(authpath + '/authenticate',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during authentication.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 200){
|
|
||||||
resolve(body)
|
|
||||||
} else {
|
|
||||||
reject(body || {code: 'ENOTFOUND'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate an access token. This should always be done before launching.
|
|
||||||
* The client token should match the one used to create the access token.
|
|
||||||
*
|
|
||||||
* @param {string} accessToken The access token to validate.
|
|
||||||
* @param {string} clientToken The launcher's client token.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Validate
|
|
||||||
*/
|
|
||||||
exports.validate = function(accessToken, clientToken){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.post(authpath + '/validate',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body: {
|
|
||||||
accessToken,
|
|
||||||
clientToken
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during validation.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 403){
|
|
||||||
resolve(false)
|
|
||||||
} else {
|
|
||||||
// 204 if valid
|
|
||||||
resolve(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invalidates an access token. The clientToken must match the
|
|
||||||
* token used to create the provided accessToken.
|
|
||||||
*
|
|
||||||
* @param {string} accessToken The access token to invalidate.
|
|
||||||
* @param {string} clientToken The launcher's client token.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Invalidate
|
|
||||||
*/
|
|
||||||
exports.invalidate = function(accessToken, clientToken){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.post(authpath + '/invalidate',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body: {
|
|
||||||
accessToken,
|
|
||||||
clientToken
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during invalidation.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 204){
|
|
||||||
resolve()
|
|
||||||
} else {
|
|
||||||
reject(body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refresh a user's authentication. This should be used to keep a user logged
|
|
||||||
* in without asking them for their credentials again. A new access token will
|
|
||||||
* be generated using a recent invalid access token. See Wiki for more info.
|
|
||||||
*
|
|
||||||
* @param {string} accessToken The old access token.
|
|
||||||
* @param {string} clientToken The launcher's client token.
|
|
||||||
* @param {boolean} requestUser Optional. Adds user object to the reponse.
|
|
||||||
*
|
|
||||||
* @see http://wiki.vg/Authentication#Refresh
|
|
||||||
*/
|
|
||||||
exports.refresh = function(accessToken, clientToken, requestUser = true){
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
request.post(authpath + '/refresh',
|
|
||||||
{
|
|
||||||
json: true,
|
|
||||||
body: {
|
|
||||||
accessToken,
|
|
||||||
clientToken,
|
|
||||||
requestUser
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function(error, response, body){
|
|
||||||
if(error){
|
|
||||||
logger.error('Error during refresh.', error)
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
if(response.statusCode === 200){
|
|
||||||
resolve(body)
|
|
||||||
} else {
|
|
||||||
reject(body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -5,13 +5,12 @@
|
|||||||
const cp = require('child_process')
|
const cp = require('child_process')
|
||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const { URL } = require('url')
|
const { URL } = require('url')
|
||||||
const { getServerStatus } = require('helios-core')
|
const { MojangRestAPI, getServerStatus } = require('helios-core/mojang')
|
||||||
|
|
||||||
// Internal Requirements
|
// Internal Requirements
|
||||||
const DiscordWrapper = require('./assets/js/discordwrapper')
|
const DiscordWrapper = require('./assets/js/discordwrapper')
|
||||||
const Mojang = require('./assets/js/mojang')
|
|
||||||
const ProcessBuilder = require('./assets/js/processbuilder')
|
const ProcessBuilder = require('./assets/js/processbuilder')
|
||||||
const ServerStatus = require('./assets/js/serverstatus')
|
const { RestResponseStatus } = require('helios-core/common')
|
||||||
|
|
||||||
// Launch Elements
|
// Launch Elements
|
||||||
const launch_content = document.getElementById('launch_content')
|
const launch_content = document.getElementById('launch_content')
|
||||||
@ -166,8 +165,15 @@ const refreshMojangStatuses = async function(){
|
|||||||
let tooltipEssentialHTML = ''
|
let tooltipEssentialHTML = ''
|
||||||
let tooltipNonEssentialHTML = ''
|
let tooltipNonEssentialHTML = ''
|
||||||
|
|
||||||
try {
|
const response = await MojangRestAPI.status()
|
||||||
const statuses = await Mojang.status()
|
let statuses
|
||||||
|
if(response.responseStatus === RestResponseStatus.SUCCESS) {
|
||||||
|
statuses = response.data
|
||||||
|
} else {
|
||||||
|
loggerLanding.warn('Unable to refresh Mojang service status.')
|
||||||
|
statuses = MojangRestAPI.getDefaultStatuses()
|
||||||
|
}
|
||||||
|
|
||||||
greenCount = 0
|
greenCount = 0
|
||||||
greyCount = 0
|
greyCount = 0
|
||||||
|
|
||||||
@ -176,12 +182,12 @@ const refreshMojangStatuses = async function(){
|
|||||||
|
|
||||||
if(service.essential){
|
if(service.essential){
|
||||||
tooltipEssentialHTML += `<div class="mojangStatusContainer">
|
tooltipEssentialHTML += `<div class="mojangStatusContainer">
|
||||||
<span class="mojangStatusIcon" style="color: ${Mojang.statusToHex(service.status)};">•</span>
|
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">•</span>
|
||||||
<span class="mojangStatusName">${service.name}</span>
|
<span class="mojangStatusName">${service.name}</span>
|
||||||
</div>`
|
</div>`
|
||||||
} else {
|
} else {
|
||||||
tooltipNonEssentialHTML += `<div class="mojangStatusContainer">
|
tooltipNonEssentialHTML += `<div class="mojangStatusContainer">
|
||||||
<span class="mojangStatusIcon" style="color: ${Mojang.statusToHex(service.status)};">•</span>
|
<span class="mojangStatusIcon" style="color: ${MojangRestAPI.statusToHex(service.status)};">•</span>
|
||||||
<span class="mojangStatusName">${service.name}</span>
|
<span class="mojangStatusName">${service.name}</span>
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
@ -207,14 +213,9 @@ const refreshMojangStatuses = async function(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
loggerLanding.warn('Unable to refresh Mojang service status.')
|
|
||||||
loggerLanding.debug(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML
|
document.getElementById('mojangStatusEssentialContainer').innerHTML = tooltipEssentialHTML
|
||||||
document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML
|
document.getElementById('mojangStatusNonEssentialContainer').innerHTML = tooltipNonEssentialHTML
|
||||||
document.getElementById('mojang_status_icon').style.color = Mojang.statusToHex(status)
|
document.getElementById('mojang_status_icon').style.color = MojangRestAPI.statusToHex(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
const refreshServerStatus = async function(fade = false){
|
const refreshServerStatus = async function(fade = false){
|
||||||
|
@ -154,79 +154,6 @@ function formDisabled(v){
|
|||||||
loginRememberOption.disabled = v
|
loginRememberOption.disabled = v
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an error and returns a user-friendly title and description
|
|
||||||
* for our error overlay.
|
|
||||||
*
|
|
||||||
* @param {Error | {cause: string, error: string, errorMessage: string}} err A Node.js
|
|
||||||
* error or Mojang error response.
|
|
||||||
*/
|
|
||||||
function resolveError(err){
|
|
||||||
// Mojang Response => err.cause | err.error | err.errorMessage
|
|
||||||
// Node error => err.code | err.message
|
|
||||||
if(err.cause != null && err.cause === 'UserMigratedException') {
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.userMigrated.title'),
|
|
||||||
desc: Lang.queryJS('login.error.userMigrated.desc')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(err.error != null){
|
|
||||||
if(err.error === 'ForbiddenOperationException'){
|
|
||||||
if(err.errorMessage != null){
|
|
||||||
if(err.errorMessage === 'Invalid credentials. Invalid username or password.'){
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.invalidCredentials.title'),
|
|
||||||
desc: Lang.queryJS('login.error.invalidCredentials.desc')
|
|
||||||
}
|
|
||||||
} else if(err.errorMessage === 'Invalid credentials.'){
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.rateLimit.title'),
|
|
||||||
desc: Lang.queryJS('login.error.rateLimit.desc')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Request errors (from Node).
|
|
||||||
if(err.code != null){
|
|
||||||
if(err.code === 'ENOENT'){
|
|
||||||
// No Internet.
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.noInternet.title'),
|
|
||||||
desc: Lang.queryJS('login.error.noInternet.desc')
|
|
||||||
}
|
|
||||||
} else if(err.code === 'ENOTFOUND'){
|
|
||||||
// Could not reach server.
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.authDown.title'),
|
|
||||||
desc: Lang.queryJS('login.error.authDown.desc')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(err.message != null){
|
|
||||||
if(err.message === 'NotPaidAccount'){
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.notPaid.title'),
|
|
||||||
desc: Lang.queryJS('login.error.notPaid.desc')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unknown error with request.
|
|
||||||
return {
|
|
||||||
title: Lang.queryJS('login.error.unknown.title'),
|
|
||||||
desc: err.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unknown Mojang error.
|
|
||||||
return {
|
|
||||||
title: err.error,
|
|
||||||
desc: err.errorMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let loginViewOnSuccess = VIEWS.landing
|
let loginViewOnSuccess = VIEWS.landing
|
||||||
let loginViewOnCancel = VIEWS.settings
|
let loginViewOnCancel = VIEWS.settings
|
||||||
let loginViewCancelHandler
|
let loginViewCancelHandler
|
||||||
@ -285,16 +212,15 @@ loginButton.addEventListener('click', () => {
|
|||||||
formDisabled(false)
|
formDisabled(false)
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}).catch((err) => {
|
}).catch((displayableError) => {
|
||||||
loginLoading(false)
|
loginLoading(false)
|
||||||
const errF = resolveError(err)
|
setOverlayContent(displayableError.title, displayableError.desc, Lang.queryJS('login.tryAgain'))
|
||||||
setOverlayContent(errF.title, errF.desc, Lang.queryJS('login.tryAgain'))
|
|
||||||
setOverlayHandler(() => {
|
setOverlayHandler(() => {
|
||||||
formDisabled(false)
|
formDisabled(false)
|
||||||
toggleOverlay(false)
|
toggleOverlay(false)
|
||||||
})
|
})
|
||||||
toggleOverlay(true)
|
toggleOverlay(true)
|
||||||
loggerLogin.log('Error while logging in.', err)
|
loggerLogin.log('Error while logging in.', displayableError)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
2788
package-lock.json
generated
2788
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -23,16 +23,17 @@
|
|||||||
"node": "16.x.x"
|
"node": "16.x.x"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^2.0.1",
|
"@electron/remote": "^2.0.4",
|
||||||
"adm-zip": "^0.5.9",
|
"adm-zip": "^0.5.9",
|
||||||
"async": "^3.2.1",
|
"async": "^3.2.3",
|
||||||
"discord-rpc": "^3.2.0",
|
"discord-rpc-patch": "^4.0.1",
|
||||||
"ejs": "^3.1.6",
|
"ejs": "^3.1.6",
|
||||||
"ejs-electron": "^2.1.1",
|
"ejs-electron": "^2.1.1",
|
||||||
"electron-updater": "^4.3.9",
|
"electron-updater": "^4.6.5",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"github-syntax-dark": "^0.5.0",
|
"github-syntax-dark": "^0.5.0",
|
||||||
"helios-core": "^0.1.0-alpha.3",
|
"got": "^11.8.3",
|
||||||
|
"helios-core": "^0.1.0-alpha.5",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"node-stream-zip": "^1.15.0",
|
"node-stream-zip": "^1.15.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
@ -41,9 +42,9 @@
|
|||||||
"winreg": "^1.2.4"
|
"winreg": "^1.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"electron": "^15.2.0",
|
"electron": "^16.0.8",
|
||||||
"electron-builder": "^22.13.1",
|
"electron-builder": "^22.14.13",
|
||||||
"eslint": "^8.0.1"
|
"eslint": "^8.8.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
Loading…
Reference in New Issue
Block a user