11import uparams = require( 'uparams' ) ;
2+ import * as cloud_social_provider from '../../lib/cloud/social/provider' ;
23import * as jsurl from 'jsurl' ;
34import * as social from '../../interfaces/social' ;
45import * as uproxy_core_api from '../../interfaces/uproxy_core_api' ;
@@ -9,26 +10,6 @@ import { SocksProxy } from '../model/socks_proxy_server';
910import { VpnDevice } from '../model/vpn_device' ;
1011import { CloudSocksProxy } from './cloud_socks_proxy_server' ;
1112
12- function parseInviteUrl ( inviteUrl : string ) : social . InviteTokenData {
13- let params = uparams ( inviteUrl ) ;
14- if ( ! params || ! params [ 'networkName' ] ) {
15- throw new Error ( `networkName not found: ${ inviteUrl } ` ) ;
16- }
17- var permission : any ;
18- if ( params [ 'permission' ] ) {
19- permission = jsurl . parse ( params [ 'permission' ] ) ;
20- }
21- return {
22- v : parseInt ( params [ 'v' ] , 10 ) ,
23- networkData : JSON . parse ( jsurl . parse ( params [ 'networkData' ] ) ) ,
24- networkName : params [ 'networkName' ] ,
25- userName : params [ 'userName' ] ,
26- permission : permission ,
27- userId : params [ 'userId' ] , // undefined if no permission
28- instanceId : params [ 'instanceId' ] , // undefined if no permission
29- }
30- }
31-
3213// A local Socks server that provides access to a remote uProxy Cloud server via RTC.
3314export class UproxyServer implements Server {
3415 private instancePath : social . InstancePath ;
@@ -56,25 +37,76 @@ export class UproxyServer implements Server {
5637 }
5738}
5839
40+ // Name by which servers are saved to storage.
41+ const SERVERS_STORAGE_KEY = 'servers' ;
42+
43+ // Type of the object placed, in serialised form, in storage.
44+ type SavedServers = { [ id : string ] : SavedServer } ;
45+
46+ // A server as saved to storage.
47+ interface SavedServer {
48+ cloudTokens ?: cloud_social_provider . Invite ;
49+ }
50+
51+ // Maintains a persisted set of servers and liases with the core.
5952export class UproxyServerRepository implements ServerRepository {
60- constructor ( private core : CoreConnector , private vpnDevice : VpnDevice ) {
61- this . core . login ( {
62- network : 'Cloud' ,
63- loginType : uproxy_core_api . LoginType . INITIAL ,
64- } ) ;
53+ constructor (
54+ private storage : Storage ,
55+ // Must already be logged into social networks.
56+ private core : CoreConnector ,
57+ private vpnDevice : VpnDevice ) { }
58+
59+ public getServers ( ) {
60+ const servers = this . loadServers ( ) ;
61+ return Promise . all ( Object . keys ( servers ) . map ( ( host ) => {
62+ return this . notifyCoreOfServer ( servers [ host ] . cloudTokens ) ;
63+ } ) ) ;
6564 }
6665
67- public addServer ( code : AccessCode ) : Promise < Server > {
68- let token = parseInviteUrl ( code ) ;
69- if ( ! token ) {
70- return Promise . reject ( `Failed to parse access code: ${ code } ` ) ;
66+ private loadServers ( ) : SavedServers {
67+ return JSON . parse ( this . storage . getItem ( SERVERS_STORAGE_KEY ) ) || { } ;
68+ }
69+
70+ // Saves a server to storage, merging it with any already found there.
71+ // Returns true if the server was not already in storage.
72+ private saveServer ( cloudTokens : cloud_social_provider . Invite ) {
73+ const savedServers = this . loadServers ( ) ;
74+ savedServers [ cloudTokens . host ] = {
75+ cloudTokens : cloudTokens
76+ } ;
77+ this . storage . setItem ( SERVERS_STORAGE_KEY , JSON . stringify ( savedServers ) ) ;
78+ }
79+
80+ public addServer ( accessCode : AccessCode ) {
81+ // This is inspired by ui.ts but note that uProxy Air only
82+ // supports v2 access codes which have just three fields:
83+ // - v
84+ // - networkName
85+ // - networkData
86+ // TODO: accept only cloud access codes
87+ const params : social . InviteTokenData = uparams ( accessCode ) ;
88+ if ( ! ( params || params . v ||
89+ params . networkName || params . networkData ) ) {
90+ return Promise . reject ( new Error ( 'could not decode URL' ) ) ;
7191 }
72- // TODO: Do I need to wait for core.login()?
92+
93+ const cloudTokens : cloud_social_provider . Invite = JSON . parse (
94+ jsurl . parse ( < string > params . networkData ) ) ;
95+ this . saveServer ( cloudTokens ) ;
96+ // TODO: only notify the core when connecting, and delete it afterwards
97+ return this . notifyCoreOfServer ( cloudTokens ) ;
98+ }
99+
100+ private notifyCoreOfServer ( cloudTokens : cloud_social_provider . Invite ) {
73101 return this . core . acceptInvitation ( {
74- network : { name : 'Cloud' } ,
75- tokenObj : token
102+ network : {
103+ name : 'Cloud'
104+ } ,
105+ tokenObj : {
106+ networkData : cloudTokens ,
107+ }
76108 } ) . then ( ( ) => {
77- let proxy = new CloudSocksProxy ( this . core , ( token . networkData as any ) . host ) ;
109+ let proxy = new CloudSocksProxy ( this . core , cloudTokens . host ) ;
78110 return new UproxyServer ( proxy , this . vpnDevice , proxy . getRemoteIpAddress ( ) ) ;
79111 } ) ;
80112 }
0 commit comments