Skip to content

Commit

Permalink
➕ Add support for demo accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
Creative-Difficulty committed Sep 18, 2023
1 parent b98709a commit a4b8ff5
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 53 deletions.
16 changes: 11 additions & 5 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { redirect, type Handle } from '@sveltejs/kit';
import type { CapitalComUserAccounts } from "$lib/types"
import { UserCST, UserXSecurityToken } from "$lib/stores";
import type { BaseAPIURLType, CapitalComUserAccounts } from "$lib/types"
import { UserCST, UserXSecurityToken, BaseAPIURL } from "$lib/stores";

let userCST: string;
UserCST.subscribe((value: string) => {
Expand All @@ -12,15 +12,21 @@ UserXSecurityToken.subscribe((value: string) => {
userXSecurityToken = value;
});

let baseAPIURL: BaseAPIURLType = "";
BaseAPIURL.subscribe((value: BaseAPIURLType) => {
baseAPIURL = value;
});

export const handle: Handle = (async ({ event, resolve }) => {
if (event.url.pathname.startsWith("/dashboard") || event.url.pathname.startsWith("/api") || event.url.pathname === "/") {
if(event.url.pathname.startsWith("/dashboard") || event.url.pathname.startsWith("/api") && !event.url.pathname.startsWith("/api/selectaccount") || event.url.pathname === "/") {
const capitalComCST = event.cookies.get("CAPITALCOM-CST");
const capitalComSecurityToken = event.cookies.get("CAPITALCOM-X-SECURITY-TOKEN");

if(capitalComCST === undefined || capitalComSecurityToken === undefined) {
if(capitalComCST === undefined || capitalComSecurityToken === undefined || baseAPIURL === "") {
throw redirect(302, "/login");
}
const response: Response = await fetch("https://api-capital.backend-capital.com/api/v1/accounts", {

const response: Response = await fetch(`${baseAPIURL}/api/v1/accounts`, {
method: "GET",
headers: {
"X-SECURITY-TOKEN": capitalComSecurityToken!,
Expand Down
4 changes: 3 additions & 1 deletion src/lib/stores.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { writable, type Writable } from "svelte/store";
import type { BaseAPIURLType } from "./types";

export const UserCST: Writable<string> = writable("");
export const UserXSecurityToken: Writable<string> = writable("");
export const UserXSecurityToken: Writable<string> = writable("");
export const BaseAPIURL: Writable<BaseAPIURLType> = writable("");
8 changes: 8 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface Account {
accountName: string;
preferred: boolean;
accountType: string;
demo?: boolean;
}

export interface CapitalComTradeHistoryResponse {
Expand Down Expand Up @@ -119,4 +120,11 @@ export interface SwitchAccountsResponse {
hasActiveDemoAccounts?: boolean
hasActiveLiveAccounts?: boolean
errorCode?: string
}

export type BaseAPIURLType = "https://api-capital.backend-capital.com" | "https://demo-api-capital.backend-capital.com" | ""

export interface SelectUserAPIRequestBody {
isDemo: boolean;
selectedAccount: string;
}
4 changes: 2 additions & 2 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script lang="ts">
import { page } from "$app/stores";
import type { CapitalComPingResponse } from "$lib/types";
import { UserCST, UserXSecurityToken } from "$lib/stores";
import { BaseAPIURL, UserCST, UserXSecurityToken } from "$lib/stores";
import "../app.postcss";
import { redirect } from "@sveltejs/kit";
import { Toaster } from "svelte-french-toast";
if($page.route.id?.startsWith("/dashboard")) {
setInterval(async () => {
const response: Response = await fetch("https://api-capital.backend-capital.com/api/v1/ping", {
const response: Response = await fetch(`${$BaseAPIURL}/api/v1/ping`, {
method: "GET",
headers: {
"X-SECURITY-TOKEN": $UserXSecurityToken.toString(),
Expand Down
13 changes: 9 additions & 4 deletions src/routes/api/getbalance/+server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { json, redirect, type RequestHandler } from '@sveltejs/kit';
import type { CapitalComTradeDetailsResponse, CapitalComTradeHistoryResponse } from "$lib/types";
import { UserCST, UserXSecurityToken } from "$lib/stores";
import type { BaseAPIURLType, CapitalComTradeDetailsResponse, CapitalComTradeHistoryResponse } from "$lib/types";
import { BaseAPIURL, UserCST, UserXSecurityToken } from "$lib/stores";

let userCST: string;
UserCST.subscribe((value: string) => {
Expand All @@ -12,12 +12,17 @@ UserXSecurityToken.subscribe((value: string) => {
userXSecurityToken = value;
});

let baseAPIURL: BaseAPIURLType = "";
BaseAPIURL.subscribe((value: BaseAPIURLType) => {
baseAPIURL = value;
});


export const GET = (async ({ cookies }) => {
const capitalComCST = cookies.get("CAPITALCOM-CST");
const capitalComSecurityToken = cookies.get("CAPITALCOM-X-SECURITY-TOKEN");
if(userCST !== capitalComCST || userXSecurityToken !== capitalComSecurityToken) { throw redirect(302, "/login") }
const response: Response = await fetch("https://api-capital.backend-capital.com/api/v1/history/activity?type=POSITION&lastPeriod=86400", {
const response: Response = await fetch(`${baseAPIURL}/api/v1/history/activity?type=POSITION&lastPeriod=86400`, {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
Expand All @@ -32,7 +37,7 @@ export const GET = (async ({ cookies }) => {

await Promise.all(parsedResponse.activities!.map(async trade => {
if(trade.source === "USER" && trade.type === "POSITION") {
const tradeDetailsResponse: Response = await fetch(`https://api-capital.backend-capital.com/api/v1/positions/${trade.dealId}`, {
const tradeDetailsResponse: Response = await fetch(`${baseAPIURL}/api/v1/positions/${trade.dealId}`, {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
Expand Down
13 changes: 9 additions & 4 deletions src/routes/api/getrecenttrades/+server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { json, redirect, type RequestHandler } from '@sveltejs/kit';
import type { CapitalComTradeDetailsResponse, CapitalComTradeHistoryResponse } from "$lib/types";
import { UserCST, UserXSecurityToken } from "$lib/stores";
import type { BaseAPIURLType, CapitalComTradeDetailsResponse, CapitalComTradeHistoryResponse } from "$lib/types";
import { BaseAPIURL, UserCST, UserXSecurityToken } from "$lib/stores";

let userCST: string;
UserCST.subscribe((value: string) => {
Expand All @@ -12,12 +12,17 @@ UserXSecurityToken.subscribe((value: string) => {
userXSecurityToken = value;
});

let baseAPIURL: BaseAPIURLType = "";
BaseAPIURL.subscribe((value: BaseAPIURLType) => {
baseAPIURL = value;
});


export const GET = (async ({ cookies }) => {
const capitalComCST = cookies.get("CAPITALCOM-CST");
const capitalComSecurityToken = cookies.get("CAPITALCOM-X-SECURITY-TOKEN");
if(userCST !== capitalComCST || userXSecurityToken !== capitalComSecurityToken) { throw redirect(302, "/login") }
const response: Response = await fetch("https://api-capital.backend-capital.com/api/v1/history/activity?type=POSITION&lastPeriod=86400", {
const response: Response = await fetch(`${baseAPIURL}/api/v1/history/activity?type=POSITION&lastPeriod=86400`, {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
Expand All @@ -32,7 +37,7 @@ export const GET = (async ({ cookies }) => {

await Promise.all(parsedResponse.activities!.map(async trade => {
if(trade.source === "USER" && trade.type === "POSITION") {
const tradeDetailsResponse: Response = await fetch(`https://api-capital.backend-capital.com/api/v1/positions/${trade.dealId}`, {
const tradeDetailsResponse: Response = await fetch(`${baseAPIURL}/api/v1/positions/${trade.dealId}`, {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
Expand Down
102 changes: 73 additions & 29 deletions src/routes/api/selectaccount/+server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { json, redirect, type RequestHandler } from '@sveltejs/kit';
import type { CapitalComUserAccounts, SwitchAccountsResponse } from "$lib/types";
import { UserCST, UserXSecurityToken } from "$lib/stores";
import type { CapitalComUserAccounts, SelectUserAPIRequestBody, SwitchAccountsResponse } from "$lib/types";
import { UserCST, UserXSecurityToken, BaseAPIURL } from "$lib/stores";

let userCST: string;
UserCST.subscribe((value: string) => {
Expand All @@ -22,40 +22,84 @@ export const POST: RequestHandler = (async ({ cookies, request }) => {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
"CST": userCST,
"Content-Type" : "application/json"
"CST": userCST
},
redirect: "follow"
});

let parsedUserAccountsResponse: CapitalComUserAccounts = await userAccountsResponse.json();
if(parsedUserAccountsResponse.errorCode !== undefined) { console.error(`Error while getting all acounts for user: ${parsedUserAccountsResponse.errorCode}`); return json({ error: `Error while getting all acounts for user: ${parsedUserAccountsResponse.errorCode}` }, { status: 500 }); }
if(parsedUserAccountsResponse.accounts?.length === 0) { console.error(`User has no accounts!: ${parsedUserAccountsResponse}`); return json({ error: "The selected account does not have any trading accounts." }, { status: 500 }); }

const userDemoAccountsResponse: Response = await fetch("https://demo-api-capital.backend-capital.com/api/v1/accounts", {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
"CST": userCST
},
redirect: "follow"
});

let submittedAccountName = JSON.parse((await request.text())).selectedAccount;
if(parsedUserAccountsResponse.accounts!.find(account => account.accountName === submittedAccountName) !== undefined) {
const parsedSwitchAccountResponse: SwitchAccountsResponse = await (await fetch("https://api-capital.backend-capital.com/api/v1/session", {
method: "PUT",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
"CST": userCST,
"Content-Type" : "application/json"
},
body: JSON.stringify({
accountId: parsedUserAccountsResponse.accounts!.find(account => account.accountName === submittedAccountName)?.accountId
}),
redirect: "follow"
})).json();

if(parsedSwitchAccountResponse.errorCode !== undefined && parsedSwitchAccountResponse.errorCode !== "error.not-different.accountId") {
console.error(`Error while selecting account: ${parsedSwitchAccountResponse.errorCode!}`); return json({ error: `Error while switching account: ${parsedSwitchAccountResponse.errorCode!}` }, { status: 500 });
} else if(parsedSwitchAccountResponse.errorCode === "error.not-different.accountId") {
return json({ success: true, msg: `Already signed in to ${submittedAccountName}.` }, { status: 200 });
let parsedDemoUserAccountsResponse: CapitalComUserAccounts = await userDemoAccountsResponse.json();

if(parsedUserAccountsResponse.errorCode !== undefined) { console.error(`Error while getting all accounts for user: ${parsedUserAccountsResponse.errorCode}`); return json({ error: `Error while getting all accounts for user: ${parsedUserAccountsResponse.errorCode}` }, { status: 500 }); }
if(parsedDemoUserAccountsResponse.errorCode !== undefined) { console.error(`Error while getting all accounts for user: ${parsedDemoUserAccountsResponse.errorCode}`); return json({ error: `Error while getting all accounts for user: ${parsedDemoUserAccountsResponse.errorCode}` }, { status: 500 }); }
if(parsedDemoUserAccountsResponse.accounts?.length === 0 && parsedUserAccountsResponse.accounts?.length === 0) { console.log(`User has no demo or spot trading accounts: ${parsedDemoUserAccountsResponse}`); return json({ error: "The selected account does not have any trading accounts." }, { status: 500 }); }

let submittedRequestBody: SelectUserAPIRequestBody = JSON.parse((await request.clone().text()))
let submittedAccountName = submittedRequestBody.selectedAccount;
let submittedAccountIsDemo = submittedRequestBody.isDemo;

if(submittedAccountIsDemo === true) {
if(parsedDemoUserAccountsResponse.accounts!.find(account => account.accountName === submittedAccountName) !== undefined) {
const parsedSignInToAccountResponse: SwitchAccountsResponse = await (await fetch("https://demo-api-capital.backend-capital.com/api/v1/session", {
method: "PUT",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
"CST": userCST,
"Content-Type" : "application/json"
},
body: JSON.stringify({
accountId: parsedDemoUserAccountsResponse.accounts!.find(account => account.accountName === submittedAccountName)?.accountId
}),
redirect: "follow"
})).json();

if(parsedSignInToAccountResponse.errorCode !== undefined && parsedSignInToAccountResponse.errorCode !== "error.not-different.accountId") {
console.error(`Error while selecting demo account: ${parsedSignInToAccountResponse.errorCode!}`); return json({ error: `Error while switching account: ${parsedSignInToAccountResponse.errorCode!}` }, { status: 500 });
} else if(parsedSignInToAccountResponse.errorCode === "error.not-different.accountId") {
return json({ success: true, msg: `Already signed in to \"${submittedAccountName}\".` }, { status: 200 });
}

BaseAPIURL.set("https://demo-api-capital.backend-capital.com");
return json({ success: true }, { status: 200 });
} else {
return json({ error: `The selected account \"${submittedAccountName}\" does not exist.` }, { status: 500 });
}

return json({ success: true }, { status: 200 });
} else {
return json({ error: `The selected trading account "${submittedAccountName}" does not exist.`.replace("\"", "") }, { status: 500 });
if(parsedUserAccountsResponse.accounts!.find(account => account.accountName === submittedAccountName) !== undefined) {
const parsedSignInToAccountResponse: SwitchAccountsResponse = await (await fetch("https://api-capital.backend-capital.com/api/v1/session", {
method: "PUT",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
"CST": userCST,
"Content-Type" : "application/json"
},
body: JSON.stringify({
accountId: parsedUserAccountsResponse.accounts!.find(account => account.accountName === submittedAccountName)?.accountId
}),
redirect: "follow"
})).json();

if(parsedSignInToAccountResponse.errorCode !== undefined && parsedSignInToAccountResponse.errorCode !== "error.not-different.accountId") {
console.error(`Error while selecting account: ${parsedSignInToAccountResponse.errorCode!}`); return json({ error: `Error while switching account: ${parsedSignInToAccountResponse.errorCode!}` }, { status: 500 });
} else if(parsedSignInToAccountResponse.errorCode === "error.not-different.accountId") {
return json({ success: true, msg: `Already signed in to \"${submittedAccountName}\".` }, { status: 200 });
}

BaseAPIURL.set("https://api-capital.backend-capital.com");
return json({ success: true }, { status: 200 });
} else {
return json({ error: `The selected trading account \"${submittedAccountName}\" does not exist.`}, { status: 500 });
}
}

});
2 changes: 1 addition & 1 deletion src/routes/dashboard/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const ssr = false
// export const ssr = false
28 changes: 23 additions & 5 deletions src/routes/login/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CapitalComCreateSessionResponse, CapitalComUserAccounts } from "$lib/types";
import type { Account, CapitalComCreateSessionResponse, CapitalComUserAccounts } from "$lib/types";
import type { Actions } from "./$types";
import { UserCST, UserXSecurityToken } from "$lib/stores"

Expand All @@ -21,6 +21,7 @@ export const actions = {
const userPassword = formData.get("user_password")!.toString();
const userAPIKey = formData.get("user_api_key")!.toString();

//TODO: Is there such a thing as a "demo" session?
const response: Response = await fetch("https://api-capital.backend-capital.com/api/v1/session", {
method: "POST",
headers: {
Expand Down Expand Up @@ -77,16 +78,33 @@ export const actions = {
},
redirect: "follow"
});

const userDemoAccountsResponse: Response = await fetch("https://demo-api-capital.backend-capital.com/api/v1/accounts", {
method: "GET",
headers: {
"X-SECURITY-TOKEN": userXSecurityToken,
"CST": userCST
},
redirect: "follow"
});

let parsedUserAccountsResponse: CapitalComUserAccounts = await userAccountsResponse.json();
if(parsedUserAccountsResponse.errorCode !== undefined) { console.error(`Error while getting all acounts for user: ${parsedUserAccountsResponse.errorCode}`); }
if(parsedUserAccountsResponse.errorCode !== undefined) { console.error(`Error while getting accounts for user: ${parsedUserAccountsResponse.errorCode}`); }

let parsedUserDemoAccountsResponse: CapitalComUserAccounts = await userDemoAccountsResponse.json();
if(parsedUserAccountsResponse.errorCode !== undefined) { console.error(`Error while getting demo acounts for user: ${parsedUserDemoAccountsResponse.errorCode}`); }

let allUserAccounts: Array<Account> = parsedUserAccountsResponse.accounts!;

parsedUserDemoAccountsResponse.accounts!.forEach((demoAccount: Account) => {
demoAccount.demo = true;
allUserAccounts.push(demoAccount);
});

return {
"showSelectAccountDialog": true,
"accounts": parsedUserAccountsResponse.accounts
"accounts": allUserAccounts
};

// throw redirect(303, "/dashboard");
}
}
} satisfies Actions;
Loading

0 comments on commit a4b8ff5

Please sign in to comment.