const { Uri } = require("./Uri");
const { StringBuilder } = require("./StringBuilder");
// eslint-disable-next-line no-unused-vars
const { CheckContactData } = require("./CheckContactData"); //lgtm [js/unused-local-variable]
const { OneTimeVerificationKey } = require("./OneTimeVerificationKey");
const { OnlineUserStats } = require("./OnlineUserStats");
// eslint-disable-next-line no-unused-vars
const { VerificationKeyUse } = require("./VerificationKeyUse"); //lgtm [js/unused-local-variable]
const { RecordUtil } = require("./RecordUtil");
const { List } = require("./List");
const { SessionInfo } = require("./SessionInfo");
const { Dictionary } = require("./Dictionary");
const { IdUtil } = require("./IdUtil");
const { OwnerType } = require("./OwnerType");
const { TimeSpan } = require("./TimeSpan");
const { HttpMethod } = require("./HttpMethod");
const { HttpRequestMessage } = require("./HttpRequestMessage");
const { ThumbnailInfo } = require("./ThumbnailInfo");
// eslint-disable-next-line no-unused-vars
const { HttpResponseMessage } = require("./HttpResponseMessage"); //lgtm [js/unused-local-variable]
const { CancellationTokenSource } = require("./CancellationTokenSource");
const { CloudResult } = require("./CloudResult");
const { LoginCredentials } = require("./LoginCredentials");
const { AuthenticationHeaderValue } = require("./AuthenticationHeaderValue");
const { User } = require("./User");
const { Friend } = require("./Friend");
const { Message } = require("./Message");
const { ServerStatistics } = require("./ServerStatistics");
const { UserTags } = require("./UserTags");
const { Out } = require("./Out");
const { Enumerable } = require("./Enumerable");
//const { v4: uuidv4 } = require("uuid");
const { HTTP_CLIENT } = require("./HTTP_CLIENT");
const { FriendManager } = require("./FriendManager");
const { MessageManager } = require("./MessageManager");
const { TransactionManager } = require("./TransactionManager");
//const { SearchResults } = require("./SearchResults");
const { ProductInfoHeaderValue } = require("./ProductInfoHeaderValue");
const { UserSession } = require("./UserSession");
const { ServerStatus } = require("./ServerStatus");
const { Group } = require("./Group");
const { Member } = require("./Member");
const { CloudVariableDefinition } = require("./CloudVariableDefinition");
const { NeosSession } = require("./NeosSession");
const { UserStatus } = require("./UserStatus");
////const Path = require("path");
////const fs = require("fs");
const { UploadState } = require("./UploadState");
const { Submission } = require("./Submission");
const { RecordId } = require("./RecordId");
const { CloudVariable } = require("./CloudVariable");
const { NeosDB_Endpoint } = require("./NeosDB_Endpoint");
const { ExitMessage } = require("./ExitMessage");
const { CurrencyRates } = require("./CurrencyRates");
const { Membership } = require("./Membership");
const { HubPatreons } = require("./HubPatreons");
const { GitHubClient } = require("./GitHubClient");
/**
*
* @class CloudXInterface
* @param {EventEmitter} BUS - Internal Communication Use, Event Bus Plug.
* @param {string} product - Library/System Name - Metadata.
* @param {string} version - Library/System Version - Metadata.
*/
class CloudXInterface {
constructor(product, version, BUS) {
this.CloudXInterface(product, version);
/**
* @type List<Membership>
* @private*/
this._groupMemberships;
/**
* @type Dictionary<String, Member>
* @private*/
this._groupMemberInfos;
/**
* @type Dictionary<String, Group>
* @private */
this._groups;
/** @type Dictionary<Type, Dictionary<Uri, CloudResult>> */
this.cachedRecords = new Dictionary();
/** @type UserSession
* @private*/
this._currentSession;
/** @type User
* @private*/
this._currentUser;
/** @type RSACryptoServiceProvider
* @private*/
this._cryptoProvider;
/** @type AuthenticationHeaderValue
* @private*/
this._currentAuthenticationHeader;
/**
* @type Date
* @private */
this._lastSessionUpdate = new Date(0);
/** @type Date */
this.lastServerStatsUpdate = new Date(0);
/** Test.
*
* @type HttpClient
*
*/
this.HttpClient;
/**@type GitHubClient */
this.GitHub;
/** @type RSAParameters */
this.PublicKey;
/** @type Number */
this.ServerResponseTime = 0;
/** @type Date */
this.LastServerUpdate = new Date(0);
/** @type Date */
this.LastServerStateFetch = new Date(0);
/** @type Date */
this.LastLocalServerResponse = new Date(0);
/** @type String */
this.UserAgentProduct;
/** @type String */
this.UserAgentVersion;
/** @type FriendManager */
this.Friends;
/** @type MessageManager */
this.Messages;
/** @type TransactionManager */
this.Transactions;
/** @type {SessionChangedFunction} */
this.SessionChanged;
/** @type {UserUpdatedFunction} */
this.UserUpdated;
/** @type {MembershipsUpdatedFunction} */
this.MembershipsUpdated;
/** @type {GroupUpdatedFunction} */
this.GroupUpdated;
/** @type {GroupMemberUpdatedFunction} */
this.GroupMemberUpdated;
//Setup Private Properties
//this.CloudXInterface()
/**.
* Internal Events
*
* @private
*/
this.Events = BUS;
Object.defineProperties(this, {
_groupMemberships: {
value: new List(),
writable: true,
enumerable: false,
configurable: true,
},
_groupMemberInfos: {
value: new Dictionary(),
writable: true,
enumerable: false,
configurable: true,
},
_USE_CDN: {
value: false,
writable: true,
enumerable: false,
configurable: true,
},
_groups: {
value: new Dictionary(),
writable: true,
enumerable: false,
configurable: true,
},
_currentSession: {
value: new UserSession(),
configurable: true,
enumerable: false,
},
_currentUser: {
writable: true,
enumerable: false,
configurable: true,
},
_cryptoProvider: {
writable: true,
enumerable: false,
configurable: true,
},
_storageDirty: {
value: new Dictionary(),
writable: true,
enumerable: false,
configurable: true,
},
_updatingStorage: {
value: new Dictionary(),
writable: true,
enumerable: false,
configurable: true,
},
_currentAuthenticationHeader: {
value: null,
writable: true,
enumerable: false,
configurable: true,
},
_lastSessionUpdate: {
value: new Date(0),
writable: true,
enumerable: false,
configurable: true,
},
_lastServerStatsUpdate: {
value: new Date(0),
writable: true,
enumerable: false,
configurable: true,
},
lockobj: {
value: "CloudXLockObj",
enumerable: false,
configurable: true,
},
});
}
/**.
* Cloud Endpoint Types
*
* @enum {Enumerable<string>} CloudEndpoint
* @property {"Production"} Production
* @property {"Staging"} Staging
* @property {"Local"} Local
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CloudEndpoint() {
if (this._cloudEndpoint == null)
Object.defineProperty(this, "_cloudEndpoint", {
value: new Enumerable(["Production", "Staging", "Local"]),
enumerable: false,
});
return this._cloudEndpoint;
}
/**.
* Number of Retries for tasks
*
* @returns {5}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get DEFAULT_RETRIES() {
return 5;
}
/**.
* Honestly Not Sure
*
* @returns {16}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get UPLOAD_DEGREE_OF_PARALLELISM() {
return 16;
}
/**.
* Debug Setting
*
* @private
* @readonly
* @static
* @memberof CloudXInterface
*/
static get DEBUG_UPLOAD() {
return false;
}
/**.
* Debug Setting
*
* @private
* @readonly
* @static
* @memberof CloudXInterface
*/
static get DEBUG_REQUESTS() {
return false;
}
/**.
* Request Timeout
*
* @returns {30000} ms
* @readonly
* @static
* @memberof CloudXInterface
*/
static get DefaultTimeout() {
return TimeSpan.fromSeconds(30);
}
/**.
* Delays in subsequent requests to the storage system
*
* @readonly
* @static
* @returns [1,5,15,30] Update Delays
* @memberof CloudXInterface
*/
static get storageUpdateDelays() {
return [1, 5, 15, 30];
}
/**.
* JSON Header
*
* @returns {{"Content-Type": "application/json"}}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get JSON_MEDIA_TYPE() {
return {
"Content-Type": "application/json",
};
}
/**.
* How frequent to extend the user session
*
* @returns {3600}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get SESSION_EXTEND_INTERVAL() {
return 3600;
}
/**.
* Not Implimented
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get ProfilerBeginSampleCallback() {
return null;
}
/**.
* Not Implimented
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get ProfilerEndSampleCallback() {
return null;
}
/**.
* Not Implimented
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get MemoryStreamAllocator() {
return null;
}
/**
* If you need to ask your account can't use this.
*
* @returns {false}
* @protected
* @readonly
* @static
* @memberof CloudXInterface
*/
static get USE_CDN() {
return false;
}
/**
*
* @returns {"https://www.neosvr-api.com"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUDX_PRODUCTION_NEOS_API() {
return "https://www.neosvr-api.com";
}
/**
*
* @returns {"https://cloudx-staging.azurewebsites.net"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUDX_STAGING_NEOS_API() {
return "https://cloudx-staging.azurewebsites.net";
}
/**
*
* @returns {"https://cloudxstorage.blob.core.windows.net/"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUDX_NEOS_DURABLE_BLOB() {
return "https://cloudxstorage.blob.core.windows.net/";
}
/**
*
* @returns {"https://cloudxoperationalblob.blob.core.windows.net/"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUDX_NEOS_OPERATIONAL_BLOB() {
return "https://cloudxoperationalblob.blob.core.windows.net/";
}
/**
*
* @returns {"https://cloudx2.azureedge.net/"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUDX_NEOS_CDN() {
return "https://cloudx2.azureedge.net/";
}
/**
*
* @returns {"http://localhost:60612"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get LOCAL_NEOS_API() {
return "http://localhost:60612";
}
/**
*
* @returns {"http://127.0.0.1:10000/devstoreaccount1/"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get LOCAL_NEOS_BLOB() {
return "http://127.0.0.1:10000/devstoreaccount1/";
}
/**
*
* @returns {"https://cloudx2.azureedge.net/"}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUDX_NEOS_VIDEO_CDN() {
return "https://cloudx2.azureedge.net/";
}
/**.
* Not Implimented
*
* @memberof CloudXInterface
*/
ProfilerBeginSample() {
let beginSampleCallback = CloudXInterface.ProfilerBeginSampleCallback;
if (beginSampleCallback == null) return;
beginSampleCallback();
}
/**.
* Not Implimented
*
* @memberof CloudXInterface
*/
ProfilerEndSample() {
let endSampleCallback = CloudXInterface.ProfilerEndSampleCallback;
if (endSampleCallback == null) return;
endSampleCallback();
}
/**.
* Current Cloud Endpoint
*
* @returns {number} Production Endpoint
* @readonly
* @static
* @memberof CloudXInterface
*/
static get CLOUD_ENDPOINT() {
return CloudXInterface.CloudEndpoint.Production;
}
/**.
* Get the Neos Endpoint
*
* @returns {"https://www.neosvr-api.com/" | "https://cloudx-staging.azurewebsites.net/" | "https://localhost:60612/" | Error}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_API() {
switch (CloudXInterface.CLOUD_ENDPOINT) {
case CloudXInterface.CloudEndpoint.Production:
return "https://www.neosvr-api.com";
case CloudXInterface.CloudEndpoint.Staging:
return "https://cloudx-staging.azurewebsites.net";
case CloudXInterface.CloudEndpoint.Local:
return "https://localhost:60612";
default:
return new Error(
"Invalid Endpoint: " + CloudXInterface.CLOUD_ENDPOINT.toString()
);
}
}
/**.
* Return the Blob Endpoint
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_BLOB() {
switch (CloudXInterface.CLOUD_ENDPOINT) {
case CloudXInterface.CloudEndpoint.Production:
case CloudXInterface.CloudEndpoint.Staging:
case CloudXInterface.CloudEndpoint.Local:
return CloudXInterface.NEOS_CLOUD_BLOB;
default:
return new Error(
"Invalid Endpoint: " + CloudXInterface.CLOUD_ENDPOINT.toString()
);
}
}
/**.
* Return the Assets URI
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_ASSETS() {
return CloudXInterface.NEOS_BLOB + "assets/";
}
/**.
* Get the Neos CDN server
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_ASSETS_CDN() {
return "https://cloudx2.azureedge.net/assets/";
}
/**.
* Get the Neos Blob Server
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_ASSETS_BLOB() {
return "https://cloudxstorage.blob.core.windows.net/assets/";
}
/**.
* Get the Neos Thumbnail Endpoint
*
* @deprecated Temporary Legacy Support, See {@link CloudXInterface.NEOS_THUMBNAILS}
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_THUMBNAILS_OLD() {
return "https://cloudxstorage.blob.core.windows.net/thumbnails/";
}
/**.
* Get the Neos Thumbnail Endpoint
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_THUMBNAILS() {
return "https://cloudxoperationalblob.blob.core.windows.net/thumbnails/";
}
/**.
* Neos Server Info Endpoint
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_INSTALL() {
return "https://cloudx2.azureedge.net/install/";
}
/**.
* Video CDN Server
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_ASSETS_VIDEO_CDN() {
return "https://cloudx2.azureedge.net/assets/";
}
/**.
* Get the Blob
*
* @readonly
* @static
* @memberof CloudXInterface
*/
static get NEOS_CLOUD_BLOB() {
return !CloudXInterface.USE_CDN
? "https://cloudxstorage.blob.core.windows.net/"
: "https://cloudx2.azureedge.net/";
}
/**.
* Recalculate Server Ping and Response Time
*
* @readonly
* @memberof CloudXInterface
*/
get ServerStatus() {
if (
new Date(new Date() - this.LastServerStateFetch).getTime() / 1000 >=
60.0
)
return ServerStatus.NoInternet;
if (new Date(new Date() - this.LastServerUpdate).getTime() / 1000 >= 60.0) {
return ServerStatus.Down;
}
return this.ServerResponseTime > 500
? ServerStatus.Slow
: ServerStatus.Good;
}
/**.
* Overrideable function to handle Errors
*
* @abstract
* @param {...*} error
* @memberof CloudXInterface
*/
OnError(...error) {
//Overridable Error Output
throw new Error(...error);
}
/**.
* Overrideable Function to handle Debug messages
*
* @param {...*} vars - Unpredictable number of arguments
* @abstract
* @memberof CloudXInterface
*/
OnDebug(...vars) {
//Overrideable
console.log(...vars);
}
/**.
* The Current User Object
*
* @memberof CloudXInterface
* @returns {User} Logged-In User
*/
get CurrentUser() {
return this._currentUser;
}
set CurrentUser(value) {
if (value === this._currentUser) return;
let user;
if (!(value instanceof User)) user = new User(value);
else user = value;
this._currentUser = user;
let userUpdated = this.UserUpdated;
if (userUpdated == null) return;
userUpdated(this._currentUser);
}
/**.
* The Current Session Object
*
* @instance
* @returns {UserSession} Logged In Session - Contains Sensative Info
* @memberof CloudXInterface
*/
get CurrentSession() {
/**@type {UserSession} */
return this._currentSession;
}
set CurrentSession(value) {
if (value == null) {
Object.defineProperties(this, {
_currentSession: {
value: new UserSession(),
configurable: true,
enumerable: false,
},
});
return;
}
if (value === this._currentSession) return;
if (!this._currentSession)
Object.defineProperties(this, {
_currentSession: {
value: new UserSession(),
configurable: true,
enumerable: false,
},
});
if (this._currentSession.SessionToken !== value.SessionToken)
this._lastSessionUpdate = new Date();
Object.defineProperties(this, {
_currentSession: {
value,
configurable: true,
enumerable: false,
},
});
//Use the Neos Schema
Object.defineProperties(this, {
_currentAuthenticationHeader: {
value: new AuthenticationHeaderValue(
"neos", //lgtm [js/hardcoded-credentials]
value.UserId + ":" + value.SessionToken
).Authorization,
configurable: true,
enumerable: false,
},
});
//Call Event
this.OnSessionUpdated();
try {
let sessionChanged = this.sessionChanged;
if (sessionChanged == null) return;
sessionChanged(this._currentSession);
} catch (error) {
Error(
"Exception in SessionChanged: " +
(this.CurrentSession.toString() + error.toString()),
true
);
}
}
/**.
*The Curent Memberships, Will return Null if this.Update has not been run
*
* @instance
* @returns {List<Membership>}
* @memberof CloudXInterface
*/
get CurrentUserMemberships() {
return this._groupMemberships;
}
/**
*
* @instance
* @readonly
* @returns {Enumerator<Group>}
* @memberof CloudXInterface
*/
get CurrentUserGroupInfos() {
return List.ToList(
this._groups.map(function (p) {
return p.Value;
})
).GetEnumerator();
}
/**
*
* @instance
* @readonly
* @returns {Enumerator<Member>}
* @memberof CloudXInterface
*/
get CurrentUserMemberInfos() {
return List.ToList(
this._groupMemberInfos.map(function (p) {
return p.Value;
})
).GetEnumerator();
}
/**.
* Get Group from Id
*
* @param {string} groupId
* @instance
* @returns {Group}
* @memberof CloudXInterface
*/
TryGetCurrentUserGroupInfo(groupId) {
return this._groups.filter(function (item) {
return item["groupId"] === groupId;
});
}
/**.
* Get Current User Group Membershop
*
* @param {string} groupId
* @returns {Member}
* @instance
* @memberof CloudXInterface
*/
TryGetCurrentUserGroupMemberInfo(groupId) {
return this._groupMemberInfos.filter(function (item) {
return item["groupId"] === groupId;
});
}
/**.
* Get User is member of groupId
*
* @param {string} groupId
* @instance
* @memberof CloudXInterface
*/
IsCurrentUserMemberOfGroup(groupId) {
return this.TryGetCurrentUserGroupMemberInfo(groupId) != null;
}
/**
* @param {string} groupId
* @returns {Member}
* @instance
* @memberof CloudXInterface
*/
TryGetCurrentUserGroupMembership(groupId) {
return this._groupMemberInfos.filter(function (item) {
return item["groupId"] === groupId;
});
}
/**.
* Redefineable Function for Hooks
*
* @instance
* @memberof CloudXInterface
*/
OnLogin() {}
/**.
* Redefineable Function for Hooks
*
* @instance
* @memberof CloudXInterface
*/
OnLogout() {}
/**.
* Redefineable Function for Hooks
*
* @instance
* @memberof CloudXInterface
*/
OnSessionUpdated() {}
/**.
* Initializing Function, Setup local managers
*
* @param {String} [UserAgentProduct="CloudX"] Agent ie. NeosJS
* @param {String} [UserAgentVersion="0.0.0.0"] Version ie v1.5.6
*/
CloudXInterface(UserAgentProduct = "CloudX", UserAgentVersion = "0.0.0.0") {
/**@type HTTP_CLIENT */
this.HttpClient = new HTTP_CLIENT();
/**@type {FriendManager} */
this.Friends = new FriendManager(this);
this.UserAgentProduct = UserAgentProduct;
this.UserAgentVersion = UserAgentVersion;
/**@type {ProductInfoHeaderValue} */
this.UserAgent = new ProductInfoHeaderValue(
UserAgentProduct,
UserAgentVersion
);
/**@type {MessageManager} */
this.Messages = new MessageManager(this);
/**@type {TransactionManager} */
this.Transactions = new TransactionManager(this);
this.GitHub = GitHubClient;
}
/**.
* Main Update Call
*
* @instance
* @memberof CloudXInterface
*/
Update() {
if (this.CurrentSession != null) {
if (
new Date(new Date() - this._lastSessionUpdate).getTime() / 1000 >=
3600.0
) {
this.ExtendSession();
this._lastSessionUpdate = new Date();
}
}
if (
new Date(new Date() - this._lastServerStatsUpdate).getTime() / 1000 >=
10.0
) {
(async () => {
/**@private */
let cloudResult = await this.GetServerStatistics();
if (cloudResult != null) {
if (cloudResult.IsOK) {
this.ServerResponseTime =
cloudResult.Entity.ResponseTimeMilliseconds;
this.LastServerUpdate = cloudResult.Entity.LastUpdate;
}
}
this.LastServerStateFetch = new Date();
})();
this._lastServerStatsUpdate = new Date();
}
if (this.Friends) this.Friends.Update();
if (this.Messages) this.Messages.Update();
for (let keyValuePair of this._storageDirty) {
if (this._updatingStorage.TryAdd(keyValuePair.Key, true)) {
let _ownerId = keyValuePair.Key(async () => {
await this.UpdateStorage(_ownerId);
})();
}
}
}
/**.
* Does the current user potentially have API access
*
* @param {String} ownerId
* @instance
* @returns {boolean}
* @memberof CloudXInterface
*/
HasPotentialAccess(ownerId) {
switch (IdUtil.GetOwnerType(ownerId)) {
case OwnerType.Machine:
return true;
case OwnerType.User:
return ownerId === this.CurrentUser.Id;
case OwnerType.Group:
return this.CurrentUserMemberships.Any((m) => m.GroupId === ownerId);
default:
return false;
}
}
/**.
* Set the user memberhsips - Local
*
* @param {List<Membership>} memberships
* @instance
* @memberof CloudXInterface
*/
SetMemberships(memberships) {
this._groupMemberships.Clear();
this._groupMemberships.AddRange(memberships);
this.RunMembershipsUpdated();
}
/**.
* Add a user membership - Local
*
* @param {Membership} membership
* @instance
* @memberof CloudXInterface
*/
AddMembership(membership) {
this._groupMemberships.Add(membership);
this.RunMembershipsUpdated();
}
/**.
* Reset the membership cache
*
* @memberof CloudXInterface
*/
ClearMemberships() {
if (this._groupMemberships.length === 0) return;
this._groupMemberships = new List();
this.RunMembershipsUpdated();
}
/**.
* Update Membership Events
*
* @async
* @returns {Promise<void>}
* @memberof CloudXInterface
*/
async RunMembershipsUpdated() {
for (let groupMembership of this._groupMemberships) {
await this.UpdateGroupInfo(groupMembership.GroupId);
}
let membershipsUpdated = this.MembershipsUpdated;
if (membershipsUpdated == null) return;
membershipsUpdated(this._groupMemberships);
}
/**.
* Convert a neosdb:// to a http cdn url
*
* @static
* @param {Uri} neosdb
* @param {NeosDB_Endpoint} endpoint
* @returns {Uri}
* @memberof CloudXInterface
*/
static NeosDBToHttp(neosdb, endpoint) {
if (!(neosdb instanceof Uri)) neosdb = new Uri(neosdb);
let str1 = CloudXInterface.NeosDBSignature(neosdb);
let str2 = CloudXInterface.NeosDBQuery(neosdb);
let str3 = str1;
if (str2 != null) str3 = str3 + "/" + str2;
if (CloudXInterface.IsLegacyNeosDB(neosdb))
return new Uri("https://neoscloud.blob.core.windows.net/assets/" + str3);
let str4 = new String(); //lgtm [js/useless-assignment-to-local] Error Avoidance
switch (endpoint) {
case NeosDB_Endpoint.Blob:
str4 = CloudXInterface.NEOS_ASSETS_BLOB;
break;
case NeosDB_Endpoint.CDN:
str4 = CloudXInterface.NEOS_ASSETS_CDN;
break;
case NeosDB_Endpoint.VideoCDN:
str4 = CloudXInterface.NEOS_ASSETS_VIDEO_CDN;
break;
default:
str4 = CloudXInterface.NEOS_ASSETS;
break;
}
return new Uri(str4 + str3);
}
/**.
* Filter Url's - Internal
*
* @static
* @param {Uri} assetURL
* @returns {Uri|void}
* @memberof CloudXInterface
*/
static FilterNeosURL(assetURL) {
if (
assetURL.Scheme === "neosdb" &&
assetURL.Segments.length >= 2 &&
assetURL.Segments.includes(".")
)
assetURL = new Uri(
"neosdb:///" + assetURL.Segments[1].noExtension() + assetURL.Query
);
return assetURL;
}
/**
*
* @static
* @param {Uri} neosdb
* @returns {string}
* @memberof CloudXInterface
*/
static NeosDBFilename(neosdb) {
return neosdb.Segments[1] + neosdb.Query;
}
/**
*
* @static
* @param {Uri} neosdb
* @returns {string}
* @memberof CloudXInterface
*/
static NeosDBSignature(neosdb) {
return neosdb.Segments[1].noExtension();
}
/**
*
* @static
* @param {Uri} neosdb
* @returns {string}
* @memberof CloudXInterface
*/
static NeosDBQuery(neosdb) {
if (neosdb.Query == null || neosdb.Query === "") return null;
return neosdb.Query.substring(1);
}
/**.
* Thumbnail ID to HTTP
*
* @static
* @param {string} id
* @returns {string}
* @memberof CloudXInterface
*/
static NeosThumbnailIdToHttp(id) {
return new Uri(
(ThumbnailInfo.IsIdVersion2(id)
? CloudXInterface.NEOS_THUMBNAILS
: CloudXInterface.NEOS_THUMBNAILS_OLD) + id
);
}
/**.
* Check if a string is a proper {@link #uri Uri}, Returns Uri on true, null on false
*
* @static
* @param {string} url
* @returns {Uri | null}
* @memberof CloudXInterface
*/
static TryFromString(url) {
if (url == null) return null;
if (Uri.IsWellFormedUriString(url, 1)) return new Uri(url);
return null;
}
/**
* @returns {boolean}
* @static
* @param {Uri} uri
* @memberof CloudXInterface
*/
static IsValidNeosDBUri(uri) {
return !(uri.Scheme !== "neosdb") && uri.Segments.length >= 2;
}
/**.
* Check if a Uri is Legacy
*
* @static
* @param {Uri} uri
* @returns {Boolean}
* @memberof CloudXInterface
*/
static IsLegacyNeosDB(uri) {
if (uri.Scheme !== "neosdb") return false;
return uri.Segments[1].noExtension().length < 30;
}
/**.
* Make a Get Request
*
* @param {string} resource - Endpoint
* @param {TimeSpan} [timeout=null]
* @param {boolean} [throwOnError=true]
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
GET(resource, timeout = null, throwOnError = true) {
return this.RunRequest(
() => {
return this.CreateRequest(resource, HttpMethod.Get);
},
timeout,
throwOnError
);
}
/**.
* Make a Post Request
*
* @param {string} resource - Endpoint
* @param {*} entity - Content
* @param {TimeSpan} [timeout=null]
* @param {boolean} [throwOnError=true]
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
POST(resource, entity, timeout = null, throwOnError = true) {
return this.RunRequest(
() => {
let request = this.CreateRequest(resource, HttpMethod.Post);
if (entity != null) this.AddBody(request, entity);
return request;
},
timeout,
throwOnError
);
}
/* //TODO
POST_File(resource, filePath, FileMIME = null, progressIndicator = null) {
return this.RunRequest(() => {
let request = this.CreateRequest(resource, HttpMethod.Post);
//TODO this.AddFileToRequest(request, filePath, FileMIME, progressIndicator);
return request;
}, TimeSpan.fromMinutes(60.0));
}
*/
/**.
* Make a Put Request
*
* @param {string} resource - Endpoint
* @param {*} entity - Content
* @param {TimeSpan} [timeout=null]
* @param {boolean} [throwOnError=true]
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
PUT(resource, entity, timeout = null, throwOnError = true) {
return this.RunRequest(
() => {
let request = this.CreateRequest(resource, HttpMethod.Put);
this.AddBody(request, entity);
return request;
},
timeout,
throwOnError
);
}
/**.
* Make a Patch Request
*
* @param {string} resource - Endpoint
* @param {*} entity - Content
* @param {TimeSpan} [timeout=null]
* @param {boolean} [throwOnError=true]
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
PATCH(resource, entity, timeout = null, throwOnError = true) {
return this.RunRequest(
() => {
let request = this.CreateRequest(resource, HttpMethod.Patch);
this.AddBody(request, entity);
return request;
},
timeout,
throwOnError
);
}
/**.
* Make a Delete Request
*
* @param {string} resource - Endpoint
* @param {TimeSpan} [timeout=null]
* @param {boolean} [throwOnError=true]
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
DELETE(resource, timeout = null, throwOnError = true) {
return this.RunRequest(
() => {
return this.CreateRequest(resource, HttpMethod.Delete);
},
timeout,
throwOnError
);
}
/*
AddFileToRequest(request, filePath, mime = null, progressIndicator = null) {
////let fileStream = fs.readFile(filePath);
//TODO #130 AddFileToRequest
//if (mime != null)
}
*/
/**.
* Build a Http Request.
*
* Fire request with {@link #cloudxinterfacecrunrequest RunRequest}
*
* @param {string} resource - Endpoint
* @param {HttpMethod} method
* @instance
* @returns {HttpRequestMessage}
* @memberof CloudXInterface
*/
CreateRequest(resource, method) {
var flag = false;
if (!resource.startsWith("http")) {
flag = true;
resource = CloudXInterface.NEOS_API + "/" + resource;
}
var httpRequestMessage = new HttpRequestMessage(method, resource);
if ((this.CurrentSession != null) & flag) {
httpRequestMessage.Headers.Authorization = this._currentAuthenticationHeader;
}
httpRequestMessage.Headers.UserAgent = this.UserAgent.Value();
return httpRequestMessage;
}
/**.
* Add a body to a request.
*
* Internal
*
* @param {HttpRequestMessage} message
* @instance
* @param {*} entity - Content
* @memberof CloudXInterface
*/
AddBody(message, entity) {
message.Headers["Content-Type"] =
CloudXInterface.JSON_MEDIA_TYPE["Content-Type"];
if (entity) message.Content = JSON.stringify(entity);
}
/**.
* Run a {@link #httprequestmessage HttpRequest}
*
* @see CloudXInterface#CreateRequest
* @param {HttpRequestMessage} requestSource
* @param {TimeSpan} timeout
* @param {boolean} [throwOnError=false]
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
* @instance
*/
async RunRequest(requestSource, timeout, throwOnError = false) {
let request = null;
let result = null;
let exception = null;
let content;
let result1;
try {
let remainingRetries = CloudXInterface.DEFAULT_RETRIES; //lgtm [js/useless-assignment-to-local] False Positive
let delay = 250;
do {
try {
request = await requestSource();
let cancellationTokenSource = new CancellationTokenSource(
timeout || CloudXInterface.DefaultTimeout
);
if (CloudXInterface.DEBUG_REQUESTS)
this.OnDebug(request.Method, request.RequestUri);
result = await this.HttpClient.SendAsync(
request,
cancellationTokenSource.Token
);
if (CloudXInterface.DEBUG_REQUESTS)
this.OnDebug(request.Method, request.RequestUri, result.StatusCode);
} catch (error2) {
exception = error2;
}
// Handle Error Response, Will Retry after <delay>
if (
result == null ||
result.StatusCode === 429 ||
result.StatusCode === 500
) {
if (result == null) {
this.OnDebug(
`Neos.js Exception running ${request.Method} request to ${request.RequestUri}. Remaining retries: ${remainingRetries}`
);
} else if (result.StatusCode >= 500) {
result = null;
this.OnDebug(
`Neos.js Exception running ${request.Method} request to ${request.RequestUri}. Remaining retries: ${remainingRetries}`
);
}
await TimeSpan.Delay(new TimeSpan(delay)); // Wait and then retry
delay *= 2; // Double Retry Time
}
} while (result == null && remainingRetries-- > 0);
if (result == null) {
if (!throwOnError) {
result1 = new CloudResult(null, 0, null, null);
} else {
if (exception == null)
this.OnError("Failed to get response. Exception is null");
this.OnError(exception);
}
} else {
if (result.IsSuccessStatusCode) {
if (request.RequestUri.includes(CloudXInterface.NEOS_API))
this.LastLocalServerResponse = new Date();
if (typeof result.Content === "string") {
content = result.Content;
} else {
content = result.Content;
if (CloudXInterface.DEBUG_REQUESTS)
this.OnDebug(
`ENTITY for ${request.Method} - ${request.RequestUri}`
);
}
result1 = new CloudResult(null, result.StatusCode, content);
} else {
// Bad Status Code
result1 = new CloudResult(null, result.StatusCode, content);
}
}
} catch (ex) {
this.OnError(ex, true); // This is a Hard Error, Request has Failed Spectacularly for some reason and will return No value, Likely braking what called it, Suggest Throw
}
return result1;
}
/**.
* Login to a Neos Account or Set a new Password via recoverCode (Sent to Email associated with account when reset requested)
*
* @param {string} credential - UserId, Email, or Username
* @param {string} [password] - Password
* @param {string} [sessionToken] - Can be used instead of password using a valid SessionToken
* @param {string} [secretMachineId] - ALWAYS SET THIS TO A UNIQUE KEY! If this is not set, All instances of the account will be logged out
* @param {Boolean} [rememberMe] - If set, Session will be valid for 7 days instead of 1.
* @param {string} [recoverCode] - If set, password field will become the new account password
* @param {string} [seviceId]
* @param deviceId
* @instance
* @returns {Promise<CloudResult<UserSession>>}
*/
async Login(
credential,
password,
sessionToken,
secretMachineId,
rememberMe,
recoverCode,
deviceId
) {
this.Logout(false);
let credentials = new LoginCredentials();
credentials.Password = password;
credentials.RecoverCode = recoverCode;
credentials.SessionToken = sessionToken;
credentials.SecretMachineId = secretMachineId;
credentials.RememberMe = rememberMe;
credentials.UniqueDeviceID = deviceId;
if (credential.startsWith("U-")) credentials.OwnerId = credential;
else if (credential.includes("@")) credentials.Email = credential;
else credentials.Username = credential;
var result = await this.POST(
"api/userSessions",
credentials,
new TimeSpan()
);
if (result.IsOK) {
this.CurrentSession = new UserSession(result.Content);
this.CurrentUser = new User({
id: this.CurrentSession.UserId,
username: credentials.Username,
email: credentials.email,
});
await this.UpdateCurrentUserInfo();
await this.UpdateCurrentUserMemberships();
this.Friends.Update();
this.OnLogin({
CurrentUser: this.CurrentUser,
CurrentSession: this.CurrentSession,
});
} else
this.OnError("Error loging in: " + result.State + "\n" + result.Content);
return result;
}
/**.
* Extend the current session
*
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
async ExtendSession() {
return await this.PATCH("api/userSessions", null, new TimeSpan());
}
/**.
* Register a new Neos Account
*
* @param {string} username
* @param {string} email
* @param {string} password
* @param {string} deviceId
* @returns {Promise<CloudResult<User>>}
* @memberof CloudXInterface
*/
async Register(username, email, password, deviceId) {
this.Logout(false);
let UniqueDeviceIDs = new List();
UniqueDeviceIDs.Add(deviceId);
return await this.POST(
"/api/users",
new User({
username,
email,
password,
UniqueDeviceIDs,
}),
new TimeSpan()
);
}
/**.
*
*
* @param {*} email
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
*/
async RequestRecoveryCode(email) {
return await this.POST(
"/api/users/requestlostpassword",
new User({
email,
}),
new TimeSpan()
);
}
async UpdateCurrentUserInfo() {
if (this.CurrentUser.Id == null) {
return this.OnError("No current user!");
} else {
var user = await this.GetUser(this.CurrentUser.Id);
if (user.IsOK && this.CurrentUser != null) {
this.CurrentUser = user.Entity;
let patreonData = this.CurrentUser.PatreonData;
let num = new Number(0); //lgtm [js/useless-assignment-to-local] False Positive
if (
(patreonData != null
? patreonData.IsPatreonSupporter
? 1
: 0
: 0) === 0
) {
let tags = this.CurrentUser.Tags;
if (tags.size > 0)
num = tags != null ? (tags.includes(UserTags.NeosTeam) ? 1 : 0) : 0;
else num = 0;
} else num = 1;
this._USE_CDN = num !== 0;
}
return user;
}
}
async GetUser(userId) {
let cloudResult = await this.GET("api/users/" + userId, new TimeSpan());
cloudResult.Content = new User(cloudResult.Entity);
return cloudResult;
}
async GetUserByName(username) {
let cloudResult = await this.GET(
"api/users/" + username + "?byUsername=true",
new TimeSpan()
);
cloudResult.Content = new User(cloudResult.Entity);
return cloudResult;
}
async GetUsers(searchName) {
let cloudResult = await this.GET(
"api/users?name=" + Uri.EscapeDataString(searchName),
new TimeSpan()
);
let userList = new List();
for (let user of cloudResult.Entity) {
userList.Add(new User(user));
}
cloudResult.Content = userList;
return cloudResult;
}
async GetUserCached(userId) {
return await this.GetUser(userId);
}
Logout(manualLogOut) {
if (
this.CurrentSession != null &&
!this.CurrentSession.RememberMe | manualLogOut
) {
let _userId = this.CurrentSession.UserId;
let _sessionToken = this.CurrentSession.SessionToken;
(async () => {
await this.DELETE(
"api/userSessions/" + _userId + "/" + _sessionToken,
new TimeSpan()
);
})();
}
this._cryptoProvider = null;
this.PublicKey = null; // TODO RSAParameters
this.CurrentSession = null;
this.CurrentUser = null;
this.ClearMemberships();
this.Friends = new FriendManager(this);
this._USE_CDN = false;
this.OnLogout();
}
/*
SignHash(hash) {
return this._cryptoProvider; //TODO Cryptography
}
*/
/**
* @template R.
*
* @param {Uri} recordUri
* @param {R} type
* @memberof CloudXInterface
*/
async FetchRecordCached(recordUri, type) {
let dictionary = new Out();
if (!this.cachedRecords.TryGetValue(type, dictionary)) {
dictionary = new Dictionary();
this.cachedRecords.Add(type, dictionary);
}
let cloudResult = new Out();
if (dictionary.TryGetValue(recordUri, cloudResult)) return cloudResult.Out;
let cloudResult1 = await this.FetchRecord(recordUri);
let out = new Out();
this.cachedRecords.Get(type, out);
let cachedRecord = out.Out;
cachedRecord.Remove(recordUri);
cachedRecord.Add(recordUri, cloudResult1);
return cloudResult1;
}
FetchRecord(ownerId, recordId) {
if (!recordId) {
let recordUri = ownerId;
ownerId = new Out();
let recordId = new Out();
if (RecordUtil.ExtractRecordID(recordUri, ownerId, recordId))
return this.FetchRecord(ownerId.Out, recordId.Out);
let recordPath = new Out();
if (RecordUtil.ExtractRecordPath(recordUri, ownerId, recordPath))
return this.FetchRecordAtPath(ownerId.Out, recordPath.Out);
return this.OnError("Uri is not a record URI");
} else {
return this.GET(
"api/" +
CloudXInterface.GetOwnerPath(ownerId) +
"/" +
ownerId +
"/records/" +
recordId,
new TimeSpan()
);
}
}
FetchRecordIRecord(recordUri) {
var ownerId = [];
var recordId = [];
if (RecordUtil.ExtractRecordID(recordUri, ownerId, recordId))
return this.FetchRecord(ownerId.Out, recordId.Out);
var recordPath = [];
if (RecordUtil.ExtractRecordPath(recordUri, ownerId, recordPath))
return this.FetchRecordAtPath(ownerId.Out, recordPath.Out);
return this.OnError("Uri is not a record URI");
}
FetchRecordAtPath(ownerId, path) {
return this.GET(
"api/" +
CloudXInterface.GetOwnerPath(ownerId) +
"/" +
ownerId +
"/records/root/" +
path,
new TimeSpan()
);
}
GetRecordsList(ids) {
return this.POST("api/records/list", ids, new TimeSpan());
}
GetRecordsFull(ownerId, tag = null, path = null) {
let ownerPath = CloudXInterface.GetOwnerPath(ownerId);
let str = "";
if (tag != null) str = "?tag=" + Uri.EscapeDataString(tag);
if (path != null) str = "?path=" + Uri.EscapeDataString(path);
return this.GET("api/" + ownerPath + "/" + ownerId + "/records" + str);
}
/**
*
* @param {Array<string> | List<string> | string} a - Record ID's|Owner Id.
* @param {string} b - Tag.
* @param {string} c - Path.
*/
GetRecords(a, b, c) {
if (a instanceof Array) return this.GetRecordsList(List.ToList(a));
if (a instanceof List) return this.GetRecordsList(a);
return this.GetRecordsFull(a, b, c);
}
/**.
*
*
* @param {SearchParameters} search
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
FindRecords(search) {
return this.POST("/api/records/pagedSearch", search, new TimeSpan());
}
/**.
*
*
* @param {*} record
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
UpsertRecord(record) {
let resource;
switch (IdUtil.GetOwnerType(record.OwnerId)) {
case OwnerType.User:
resource =
"api/users/" + record.OwnerId + "/records/" + record.RecordId;
break;
case OwnerType.Group:
resource =
"api/groups/" + record.OwnerId + "/records/" + record.RecordId;
break;
default:
return this.OnError("Invalid record owner");
}
return this.PUT(resource, record, new TimeSpan());
}
PreprocessRecord(record) {
let resource;
switch (IdUtil.GetOwnerType(record.OwnerId)) {
case OwnerType.User:
resource =
"api/users/" +
record.OwnerId +
"/records/" +
record.RecordId +
"/preprocess";
break;
case OwnerType.Group:
resource =
"api/groups/" +
record.OwnerId +
"/records/" +
record.RecordId +
"/preprocess";
break;
default:
return this.OnError("Invalid record owner");
}
return this.POST(resource, record, new TimeSpan());
}
/*
GetPreprocessStatus(ownerId, recordId, id) {
return this.OnError("Not Implimented");
if (!recordId) {
recordId = ownerId.RecordId;
id = ownerId.PreprocessId;
ownerId = ownerId.OwnerId;
}
let resource;
switch (IdUtil.GetOwnerType(record.OwnerId)) {
case OwnerType.User:
resource =
"api/users/" +
record.OwnerId +
"/records/" +
record.RecordId +
"/preprocess/" +
id;
break;
case OwnerType.Group:
resource =
"api/groups/" +
record.OwnerId +
"/records/" +
record.RecordId +
"/preprocess/" +
id;
break;
default:
return this.OnError("Invalid record owner");
}
return this.GET(resource, record, new TimeSpan());
}
*/
/**.
*
*
* @param {*} ownerId
* @param {*} recordId
* @returns {Promise<CloudResult<any>>}
* @memberof CloudXInterface
*/
async DeleteRecord(ownerId, recordId) {
if (!recordId) {
recordId = ownerId.RecordId;
ownerId = ownerId.OwnerId;
}
let result = await this.DELETE(
"api/users/" + ownerId + "/records/" + recordId,
new TimeSpan()
);
this.MarkStorageDirty(ownerId);
return result;
}
AddTag(ownerId, recordId, tag) {
switch (IdUtil.GetOwnerType(ownerId)) {
case OwnerType.User:
return this.PUT(
"api/users/" + ownerId + "/records/" + recordId + "/tags",
tag,
new TimeSpan()
);
case OwnerType.Group:
return this.PUT(
"api/groups/" + ownerId + "/records/" + recordId + "/tags",
tag,
new TimeSpan()
);
default:
return this.OnError("Invalid record owner");
}
}
MarkStorageDirty(ownerId) {
this._storageDirty.TryAdd(ownerId, true);
}
async UpdateStorage(ownerId) {
if (this.CurrentUser != null) {
let ownerType = IdUtil.GetOwnerType(ownerId);
let _signedUserId = this.CurrentUser.Id;
let numArray = CloudXInterface.storageUpdateDelays;
for (let index = 0; index < numArray.length; index++) {
await TimeSpan.Delay(TimeSpan.fromSeconds(numArray[index]));
if (!(this.CurrentUser.Id !== _signedUserId)) {
if (ownerType === OwnerType.User) {
await this.UpdateCurrentUserInfo();
} else {
await this.UpdateGroupInfo(ownerId);
}
} else break;
}
numArray = null;
_signedUserId = null;
}
this._updatingStorage.TryRemove(ownerId, []);
}
async FetchGlobalAssetInfo(hash) {
return await this.GET("api/assets/" + hash.toLowerCase(), new TimeSpan());
}
async FetchUserAssetInfo(hash) {
return await this.FetchAssetInfo(this.CurrentUser.Id, hash);
}
async FetchAssetInfo(ownerId, hash) {
switch (IdUtil.GetOwnerType(ownerId)) {
case OwnerType.User:
return await this.GET(
"api/users/" + ownerId + "/assets/" + hash,
new TimeSpan()
);
case OwnerType.Group:
return await this.GET(
"api/groups/" + ownerId + "/assets/" + hash,
new TimeSpan()
);
default:
return this.OnError("Invalid ownerId");
}
}
async RegisterAssetInfo(assetInfo) {
switch (IdUtil.GetOwnerType(assetInfo.OwnerId)) {
case OwnerType.User:
return await this.PUT(
"api/users/" + assetInfo.OwnerId + "/assets/" + assetInfo.AssetHash,
assetInfo,
new TimeSpan()
);
case OwnerType.Group:
return await this.PUT(
"api/groups/" + assetInfo.OwnerId + "/assets/" + assetInfo.AssetHash,
assetInfo,
new TimeSpan()
);
default:
return this.OnError("Invalid ownerId");
}
}
GetAssetBaseURL(ownerId, hash, variant) {
hash = hash.toLowerCase();
let str = hash;
if (variant != null) str += "&" + variant;
switch (IdUtil.GetOwnerType(ownerId)) {
case OwnerType.User:
return "api/users/" + ownerId + "/assets/" + str;
case OwnerType.Group:
return "api/groups/" + ownerId + "/assets/" + str;
default:
return this.OnError("Invalid ownerId");
}
}
async UploadAsset(
ownerId,
signature,
variant,
assetPath,
retries = 5,
progressIndicator = null
) {
let cloudResult = await this.BeginUploadAsset(
ownerId,
signature,
variant,
assetPath,
retries,
progressIndicator,
new Number()
);
if (!cloudResult.isOK) return cloudResult;
return await this.WaitForAssetFinishProcessing(cloudResult.Entity);
}
/*
EnqueueChunk(baseUrl, fileName, buffer, processingBuffers) {
buffer.task = this.RunRequest(() => {}); //TODO Wtf is this
}
*/
async TakeFinishedBuffer() {
//TODO TakeFinishedBuffer
}
/*
async BeginUploadAsset(
ownerId,
signature,
variant,
assetPath,
retries = 5,
progressIndicator = null,
bytes = null //TODO
) {
// let fileName = Path.GetFileName(assetPath);
//TODO finish
}
*/
async WaitForAssetFinishProcessing(assetUpload) {
let baseUrl =
this.GetAssetBaseURL(
assetUpload.OwnerId,
assetUpload.Signature,
assetUpload.Variant
) + "/chunks";
let cloudResult;
let condition = true;
while (condition) {
cloudResult = await this.GET(baseUrl, new TimeSpan());
if (
!cloudResult.IsError &&
cloudResult.Entity.UploadState !== UploadState.Uploaded &&
cloudResult.Entity.UploadState !== UploadState.Failed
)
await TimeSpan.Delay(new TimeSpan(250));
else condition = false;
}
return cloudResult;
}
/* //TODO
UploadThumbnail(path) {
return this.POST_File("api/thumbnails", path, "image/webp", null);
}
*/
ExtendThumbnailLifetime(thumbnail) {
return this.PATCH("api/thumbnails", thumbnail, new TimeSpan());
}
DeleteThumbnail(thumbnail) {
return this.DELETE(
"api/thumbnails/" + thumbnail.Id + "/" + thumbnail.Key,
new TimeSpan()
);
}
/**.
*
*
* @param {*} groupId
* @returns {Promise<CloudResult<Group>>}
* @memberof CloudXInterface
*/
async GetGroup(groupId) {
var res = await this.GET("api/groups/" + groupId, new TimeSpan());
res.Content = new Group(res.Entity);
return res;
}
async GetGroupCached(groupId) {
return await this.GetGroup(groupId);
}
async CreateGroup(group) {
return await this.POST("api/groups", group, new TimeSpan());
}
async AddGroupMember(member) {
return await this.POST(
"api/groups/" + member.GroupId + "/members",
member,
new TimeSpan()
);
}
/**.
*
*
* @param {*} member
* @returns {Promise<CloudResult<Member>>}
* @memberof CloudXInterface
*/
async DeleteGroupMember(member) {
return await this.DELETE(
"api/groups/" + member.GroupId + "/members/" + member.UserId,
new TimeSpan()
);
}
/**.
*
*
* @param {*} groupId
* @param {*} userId
* @returns {Promise<CloudResult<Member>>}
* @memberof CloudXInterface
*/
async GetGroupMember(groupId, userId) {
var res = await this.GET(
"api/groups/" + groupId + "/members/" + userId,
new TimeSpan()
);
res.Content = new Member(res.Entity);
return res;
}
/**.
*
*
* @param {*} groupId
* @returns{Promise<CloudResult<List<Member>>>}
* @memberof CloudXInterface
*/
async GetGroupMembers(groupId) {
var res = await this.GET(
"api/groups/" + groupId + "/members",
new TimeSpan()
);
let MemberList = new List();
for (let Member of res.Entity) {
MemberList.Add(Member);
}
res.Content = MemberList;
return res;
}
async UpdateCurrentUserMemberships() {
let groupMemberships = await this.GetUserGroupMemberships();
if (groupMemberships.IsOK) this.SetMemberships(groupMemberships.Entity);
return groupMemberships;
}
async GetUserGroupMemberships(userId) {
if (!userId) return await this.GetUserGroupMemberships(this.CurrentUser.Id);
let response = await this.GET(
"api/users/" + userId + "/memberships",
new TimeSpan()
);
var GroupInfo = new List();
for (let Group of response.Entity) {
GroupInfo.Add(new Membership(Group));
}
response.Content = GroupInfo;
return response;
}
/**
*
* @returns {Task}
* @param {string} groupId
* @memberof CloudXInterface
*/
async UpdateGroupInfo(groupId) {
let group = this.GetGroup(groupId);
let memberTask = this.GetGroupMember(groupId, this.CurrentUser.Id);
let groupResult = await group;
let cloudResult = await memberTask;
if (groupResult.IsOK) {
this._groups.TryRemove(groupId);
this._groups.Add(groupId, groupResult.Entity);
let groupUpdated = this.GroupUpdated;
if (groupUpdated != null) groupUpdated(groupResult.Entity);
}
if (!cloudResult.IsOK) return;
this._groupMemberInfos.TryRemove(groupId);
this._groupMemberInfos.Add(groupId, cloudResult.Entity);
let groupMemberUpdated = this.GroupMemberUpdated;
if (groupMemberUpdated == null) return;
groupMemberUpdated(cloudResult.Entity);
}
async UpsertSubmission(groupId, ownerId, recordId, feature = false) {
return await this.PUT(
"api/groups/" + groupId + "/submissions",
new Submission(
{
groupId,
feature,
targetRecordId: new RecordId(ownerId, recordId),
},
new TimeSpan()
)
);
}
async DeleteSubmission(groupId, submissionId) {
return await this.DELETE(
"api/groups/" + groupId + "/submissions/" + submissionId,
new TimeSpan()
);
}
static GetOwnerPath(ownerId) {
switch (IdUtil.GetOwnerType(ownerId)) {
case OwnerType.User:
return "users";
case OwnerType.Group:
return "groups";
default:
return new Error("Invalid Owner Type: " + ownerId);
}
}
/**.
*
*
* @param {CloudVariableDefinition} definition
* @returns {Promise<CloudResult<CloudVariableDefinition>>}
* @memberof CloudXInterface
*/
async UpsertVariableDefinition(definition) {
return await this.PUT(
"api/" +
CloudXInterface.GetOwnerPath(definition.DefinitionOwnerId) +
"/" +
definition.DefinitionOwnerId +
"/vardefs/" +
definition.Subpath,
definition,
new TimeSpan()
).then((b) => {
b.Content = new CloudVariableDefinition(b.Entity);
});
}
async ReadGlobalVariable(path) {
return await this.ReadVariable("GLOBAL", path);
}
async ReadVariable(ownerId, path) {
if (!path) return await this.ReadVariable(this.CurrentUser.Id, ownerId);
let cloudXInterface = this;
let resource;
if (ownerId === "GLOBAL") resource = "api/globalvars/" + path;
else
resource =
"api/" +
CloudXInterface.GetOwnerPath(ownerId) +
"/" +
ownerId +
"/vars/" +
path;
let cloudResult = await cloudXInterface.GET(resource, new TimeSpan());
if (cloudResult.IsOK) {
switch (cloudResult.Entity.Value) {
case null:
break;
default:
return new CloudResult(
"error",
cloudResult.State,
cloudResult.Content
);
//TODO Deserialize
}
}
return new CloudResult("default", cloudResult.State, cloudResult.Content);
}
SerializatiOnErrorHandeler() {}
/**
* Write a Variable
* - If ownerId is Omitted and arguments are shifter, CurrentUser will be used.
*
* @template T.
* @param {string | string} ownerId - OwnerID | Path.
* @param {string | T} path - Path or T.
* @param {T} [value]
* @memberof CloudXInterface
*/
async WriteVariable(ownerId, path, value) {
if (!value)
return await this.WriteVariable(this.CurrentUser.Id, ownerId, path);
return await this.PUT(
"api/" +
CloudXInterface.GetOwnerPath(ownerId) +
"/" +
ownerId +
"/vars/" +
path,
new CloudVariable(
{
value: JSON.stringify(value),
},
new TimeSpan()
)
);
}
async DeleteVariable(ownerId, path) {
if (!path) return await this.DeleteVariable(this.CurrentUser.Id, ownerId);
return await this.DELETE(
"api/" + CloudXInterface.GetOwnerPath(ownerId) + "/vars/" + path,
new TimeSpan()
);
}
/**
*
* @returns {Promise<CloudResult>}
* @param {Visit} visit
* @memberof CloudXInterface
*/
async LogVisit(visit) {
return await this.POST("api/visits", visit, new TimeSpan());
}
/**
*
* @returns {Promise<CloudResult<NeosSession>>}
* @param {NeosSession} session
* @memberof CloudXInterface
*/
async CreateNeosSession(session) {
return await this.POST("api/neosSessions", session, new TimeSpan()).then(
(b) => {
b.Content = new NeosSession(b.Entity);
return b;
}
);
}
/**
*
* @param {NeosSession} session
* @returns {Promise<CloudResult<NeosSession>>}
* @memberof CloudXInterface
*/
async PatchNeosSession(session) {
return await this.PATCH("api/neosSessions", session, new TimeSpan()).then(
(b) => {
b.Content = new NeosSession(b.Entity);
return b;
}
);
}
/**.
* Get User Status
*
* @param {string} userId
* @returns {Promise<CloudResult<UserStatus>>}
* @memberof CloudXInterface
*/
async GetStatus(userId) {
return await this.GET(
"api/users/" + userId + "/status",
new TimeSpan()
).then((b) => {
b.Content = new UserStatus(b.Entity);
return b;
});
}
/**.
* Get Online User Statistics
*
* @returns {OnlineUserStats}
* @memberof CloudXInterface
*/
async GetOnlineUserStats() {
return new OnlineUserStats(
(await this.GET("api/stats/onlineUserStats", new TimeSpan())).Entity
);
}
/**
*
* @returns {Promise<HubPatreons>}
* @memberof CloudXInterface
*/
async GetHubPatreons() {
return new HubPatreons(
(await this.GET("api/stats/hubPatrons", new TimeSpan())).Entity
);
}
/**.
* Get a random Exit Message
*
* @returns {ExitMessage}
* @memberof CloudXInterface
*/
async GetRandomExitMessage() {
return new ExitMessage(
(await this.GET("api/exitMessage", new TimeSpan())).Entity
);
}
/**.
* Get a list of Transaction Rates
*
* @param {string} [baseCurrency="USD"]
* @returns {CurrencyRates}
* @memberof CloudXInterface
*/
async GetCurrencyRates(baseCurrency = "USD") {
return new CurrencyRates(
(
await this.GET(
"https://api.exchangeratesapi.io/latest?base=" + baseCurrency,
new TimeSpan()
)
).Entity
);
}
/**.
* Update the User Status
* -If not userId is supplied, uses Current User, Refer to Examples
*
* @param {string | UserStatus} userId
* @param {UserStatus} [status]
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
* @example
* await UpdateStatus(userId, UserStatus)
* await UpdateStatus(UserStatus)
*/
async UpdateStatus(userId, status) {
if (!status) return await this.UpdateStatus(this.CurrentUser.Id, userId);
return await this.PUT(
"api/users/" + userId + "/status",
status,
new TimeSpan()
);
}
/**.
* Update the User Profile
*
* @param {string | UserProfile} userId
* @param {UserProfile} [profile]
* @memberof CloudXInterface
* @returns {Promise<CloudResult>}
* @example
* await UpdateProfile(userId, UserProfile)
* await UpdateProfile(UserProfile)
*/
async UpdateProfile(userId, profile) {
if (!profile) {
return await this.UpdateProfile(this.CurrentUser.Id, userId);
}
return await this.PUT(
"api/users/" + userId + "/profile",
profile,
new TimeSpan()
);
}
/**.
*
*
* @param {string | Date} userId
* @param {Date} [lastStatusUpdate = null]
* @param count
* @memberof CloudXInterface
* @returns {Promise<CloudResult<List<Friend>>>}
*/
async GetFriends(userId, lastStatusUpdate = null, count = 0) {
if (count > 10) return new List();
if (typeof userId !== "string")
return await this.GetFriends(this.CurrentUser.Id, userId, ++count);
let str = "";
if (lastStatusUpdate)
str += "?lastStatusUpdate=" + encodeURI(lastStatusUpdate.toUTCString());
return await this.GET(
"api/users/" + userId + "/friends" + str,
new TimeSpan()
).then((b) => {
if (b.IsError) return b;
let a = new List();
for (let item of b.Entity) a.Add(new Friend(item));
b.Content = a;
return b;
});
}
/**.
* Add a Friend Record
*
* @param {Friend} friend
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
*/
async UpsertFriend(friend) {
if (String.IsNullOrWhiteSpace(friend.OwnerId))
return this.OnError("Argument Acception: friend.OwnerId");
if (String.IsNullOrWhiteSpace(friend.FriendUserId))
return this.OnError("Argument Acception: friend.FriendUserId");
return await this.PUT(
"api/users/" + friend.OwnerId + "/friends/" + friend.FriendUserId,
friend,
new TimeSpan()
);
}
/**.
* Remove a Friend
*
* @param {Friend|string} friend
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
*/
async DeleteFriend(friend) {
if (typeof friend === "string") friend = this.Friends.GetFriend(friend);
if (String.IsNullOrWhiteSpace(friend.OwnerId))
return this.OnError("Argument Acception: friend.OwnerId");
if (String.IsNullOrWhiteSpace(friend.FriendUserId))
return this.OnError("Argument Acception: friend.FriendUserId");
return await this.DELETE(
"api/users/" + friend.OwnerId + "/friends/" + friend.FriendUserId,
friend,
new TimeSpan()
);
}
/**
* Send a Message Object
* - Requires Sender be friends with Recipient.
*
* @param {Message} message
* @returns {Promise<CloudResult<Message>>}
* @memberof CloudXInterface
*/
async SendMessage(message) {
return await this.POST(
"api/users/" + message.RecipientId + "/messages",
message,
new TimeSpan()
);
}
/**.
*
*
* @param {Transaction} transaction
* @returns {Promise<CloudResult<*>>}
* @memberof CloudXInterface
*/
async SendTransaction(transaction) {
return this.POST(
"api/transactions/" + transaction.Token,
transaction,
new TimeSpan()
);
}
async RequestDepositAddress() {
return this.GET(
"api/users/" + this.CurrentUser.Id + "/despositAddress",
new TimeSpan()
);
}
/**
* @private
* @param {string} baseId
* @param {VerificationKeyUse} use
* @memberof CloudXInterface
*/
async CreateKey(baseId, use) {
let str = `keyUse=${use}`;
if (!(baseId == null || baseId.trim() === "")) str += "&baseKeyId" + baseId;
return this.POST(
"api/users/" + this.CurrentUser.Id + "/onetimekeys?" + str,
null,
new TimeSpan()
).then((b) => {
b.Content = new OneTimeVerificationKey(b.Content);
return b;
});
}
/**
* Internal
* check if user is friends with atleast 1 contact.
*
* @async
* @private
* @param {CheckContactData} data
* @returns {Promise<CloudResult<boolean>>}
* @memberof CloudXInterface
*/
async CheckContact(data) {
var cloudResult = await this.POST(
"api/users/" + data.OwnerId + "/checkContact",
data,
new TimeSpan()
);
return cloudResult.State !== 200
? new CloudResult(false, cloudResult.State, cloudResult.Content)
: new CloudResult(true, cloudResult.State, null);
}
/**.
* Return all unread messages
*
* @returns {Promise<CloudResult<List<Message>>>}
* @param {Date} [fromTime=null]
* @memberof CloudXInterface
*/
async GetUnreadMessages(fromTime = null) {
return await this.GetMessages(fromTime, -1, null, true);
}
/**.
* Get message history with `user`
*
* @param {string} user
* @param {number} [maxItems=100]
* @returns {Promise<CloudResult<List<Message>>>}
* @memberof CloudXInterface
*/
async GetMessageHistory(user, maxItems = 100) {
return await this.GetMessages(new Date(0), maxItems, user, false);
}
/**.
* Get messages
*
* @param {Date} [fromTime]
* @param {Number} maxItems
* @param {String} user
* @param {Boolean} unreadOnly
* @returns {Promise<CloudResult<List<Message>>>}
* @memberof CloudXInterface
*/
async GetMessages(fromTime, maxItems, user, unreadOnly) {
let stringBuilder = new StringBuilder();
stringBuilder.Append(`?maxItems=${maxItems}`);
if (fromTime)
stringBuilder.Append(
`&fromTime=${Uri.EscapeDataString(fromTime.toUTCString())}`
);
if (user != null)
stringBuilder.Append(`&user=${Uri.EscapeDataString(user)}`);
if (unreadOnly) stringBuilder.Append("&unread=true");
return await this.GET(
`api/users/${this.CurrentUser.Id}/messages${stringBuilder.toString()}`,
new TimeSpan()
).then((b) => {
if (b.IsError) return b;
let a = new List();
for (let item of b.Entity) a.Add(new Message(item));
b.Content = a;
return b;
});
}
/**.
* Mark Messages as Read
*
* @param {List<String>} messageIds
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
*/
async MarkMessagesRead(messageIds) {
switch (messageIds[0].constructor.name) {
case "String":
return await this.PATCH(
"api/users/" + this.CurrentUser.Id + "/messages",
messageIds,
new TimeSpan()
);
case "Message":
return await this.MarkMessagesRead(messageIds.map((m) => m.Id));
}
}
/**.
*
*
* @param {SessionUpdate} update
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
*/
async UpdateSessions(update) {
return await this.PUT("api/sessions/", update, new TimeSpan());
}
/**.
*
*
* @param {string} sessionId Session Id
* @returns {Promise<CloudResult<SessionInfo>>}
* @memberof CloudXInterface
*/
async GetSession(sessionId) {
return await this.GET("api/sessions/" + sessionId, new TimeSpan()).then(
(b) => {
let Entity = new SessionInfo(b.Entity);
b.Content = Entity;
return b;
}
);
}
/**.
* Get a list of sessions
*
* @param {Date} updateSince
* @param {Boolean} includeEnded
* @param {String} compatibilityHash
* @param {String} name
* @param {String} universeId
* @param {String} hostName
* @param {String} hostId
* @param {Number} minActiveUsers
* @param {Boolean} includeEmptyHeadless
* @returns {Promise<CloudResult<List<Sessioninfo>>>}
*/
async GetSessions(
updateSince = null,
includeEnded = false,
compatibilityHash = null,
name = null,
universeId = null,
hostName = null,
hostId = null,
minActiveUsers = null,
includeEmptyHeadless = true
) {
let stringBuilder1 = new StringBuilder();
if (updateSince) {
let str =
"&updatedSince=" + Uri.EscapeDataString(updateSince.toISOString());
stringBuilder1.Append(str);
}
if (includeEnded) stringBuilder1.Append("&includeEnded=true");
if (!String.IsNullOrWhiteSpace(compatibilityHash))
stringBuilder1.Append(
"&compatibilityHash=" + Uri.EscapeDataString(compatibilityHash)
);
if (!String.IsNullOrWhiteSpace(name))
stringBuilder1.Append("&name=" + Uri.EscapeDataString(name));
if (!String.IsNullOrWhiteSpace(universeId))
stringBuilder1.Append("&universeId=" + Uri.EscapeDataString(universeId));
if (!String.IsNullOrWhiteSpace(hostName))
stringBuilder1.Append("&hostName=" + Uri.EscapeDataString(hostName));
if (!String.IsNullOrWhiteSpace(hostId))
stringBuilder1.Append("&hostId=" + Uri.EscapeDataString(hostId));
if (minActiveUsers != null)
stringBuilder1.Append("&minActiveUsers=" + minActiveUsers);
stringBuilder1.Append(
"&includeEmptyHeadless=" + (includeEmptyHeadless ? "true" : "false")
);
if (stringBuilder1.Length > 0) stringBuilder1.String[0] = "?";
let str1 = stringBuilder1.ToString();
return await this.GET("api/sessions" + str1).then((res) => {
let Content = new List();
for (let item of res.Content) Content.Add(new SessionInfo(item));
res.Content = Content;
return res;
});
}
/**.
*
*
* @returns {Promise<CloudResult>}
* @memberof CloudXInterface
*/
async Ping() {
return await this.GET("api/testing/ping", new TimeSpan());
}
NotifyOnlineInstance(machineId) {
return this.POST(
"api/stats/instanceOnline/" + machineId,
{},
new TimeSpan()
);
}
async GetOnlineInstanceCount() {
let cloudResult = await this.GET(
"api/stats/onlineInstances/",
new TimeSpan()
);
let result = new Out();
return !cloudResult.IsOK || Number.TryParseInt(cloudResult.Content, result)
? -1
: result.Out;
}
/**.
* Fetch Neos Server Statistics
*
* @returns {Promise<CloudResult<ServerStatistics>>}
* @memberof CloudXInterface
*/
async GetServerStatistics() {
try {
var request = new HttpRequestMessage(
HttpMethod.Get,
"https://cloudxstorage.blob.core.windows.net/install/ServerResponse"
);
return await this.HttpClient.SendAsync(request).then(
(httpResponseMessage) => {
if (!httpResponseMessage.IsSuccessStatusCode)
return new CloudResult(null, httpResponseMessage.StatusCode, null);
let contentLength = httpResponseMessage.Headers["content-length"];
let num = 0;
if (!(contentLength > num)) return null;
return new CloudResult(
null,
200,
new ServerStatistics(httpResponseMessage.Content)
);
}
);
} catch (error) {
this.OnError(error);
return null;
}
}
/**
*
* @returns {Promise<number>}
* @memberof CloudXInterface
*/
async GetOnlineUserCount() {
let cloudResult = await this.GET("api/stats/onlineUsers", new TimeSpan());
return !cloudResult.IsOK || !Number.parseInt(cloudResult.Content)
? -1
: Number.parseInt(cloudResult.Content);
}
async GetGithubIssue(issue_number) {
try {
return await this.GitHub.issues.get({
owner: "Neos-Metaverse",
repo: "NeosPublic",
issue_number,
});
} catch (ex) {
return null;
}
}
}
module.exports = {
CloudXInterface,
};