-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Persist servers by access key #988
Conversation
…omparison; fix alreadyadded error message
src/www/app/outline_server.ts
Outdated
return this.serverById.get(serverId); | ||
} | ||
|
||
add(accessKey: string, serverName: string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make add not take the serverName. I'd like this to be aligned with the product.
It's ok to have unnamed servers. The application/ui code should have the logic: if server has a name, use it, otherwise use the default name. This is the same approach we use for access keys in the manager.
In order to support Outline/Shadowsocks Server, you'll need to expose a getter to differentiate them.
Note that the access key may have a name, so we can leverage that for the fake servers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 76bde1c. I also changed the persistence format to encode the name. In order to support default (outlne/non-outline) server name, I added an extra
field to ShadowsocksConfig
.
src/www/app/electron_main.ts
Outdated
getPersistentServerFactory: () => { | ||
return (serverId: string, config: ServerConfig, eventQueue: EventQueue) => { | ||
getServerFactory: () => { | ||
return (serverId: string, serverName: string, config: ShadowsocksConfig, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert serverName. See my other comment for explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 76bde1c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I think we can tweak the storage format, but all the remaining changes are localized there.
src/www/app/app.ts
Outdated
@@ -543,8 +523,18 @@ export class App { | |||
this.rootEl.changePage(this.rootEl.DEFAULT_PAGE); | |||
} | |||
|
|||
// Returns the server's name, if present, or a default server name. | |||
private getServerName(server: Server): string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd name this getServerDisplayName(server)
, so it's clearer it's the name for display.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 7a6d05e.
src/www/app/outline_server.ts
Outdated
} | ||
|
||
set name(newName: string) { | ||
this.config.name = newName; | ||
} | ||
|
||
get host() { | ||
return this.config.host; | ||
get accessKey() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this used? Delete if not used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OutlineServer
needs to expose the access key so the repo can find servers by access key to prevent adding duplicate servers.
src/www/app/outline_server.ts
Outdated
public readonly createServer: OutlineServerFactory, private eventQueue: events.EventQueue, | ||
private storage: Storage) { | ||
try { | ||
migrateServerStorageToV1(this.storage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need to explicitly migrate if there were no changes.
Instead, just call loadServers(). loadServers() will have to try v1 and fallback to v0 forever anyway.
Then, on storeServers(), you write the new format when change happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in bc68e17.
src/www/app/outline_server.ts
Outdated
return cipher !== undefined && OutlineServer.SUPPORTED_CIPHERS.includes(cipher); | ||
} | ||
} | ||
|
||
// DEPRECATED: V0 server persistence format. | ||
export interface ConfigById { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps name them ServersStorageV0
and ServersStorageV1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in bc68e17.
src/www/app/outline_server.ts
Outdated
} | ||
|
||
// V1 server persistence format. | ||
export interface AccessKeyById { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A dictionary is not a very convenient format, because the type is weird. It's not a fixed schema, since you have variable fields. It's better to use a list.
Also, it seems the access key is not all we need.
I think we need something like:
type AccessKey = string;
interface ServerJson {
id: string
name?: string
accessKey: AccessKey // device key for connection. Whatever you get from the permanent redirects.
sharedAccessKey?: AccessKey // multi-user key for sharing
}
accessKey
could be name connectionAccessKey
.
sharedAccessKey
could have other names, such as originalAccessKey
.
The is_outline bit could be derived from the sharedAccessKey, or accessKey if absent.
The localStorage format under servers_v1
would be ServerJson[]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice suggestion. Done in bc68e17 (without multi-user keys). I also updated the storage migration unit tests.
src/www/app/config.ts
Outdated
@@ -19,4 +19,5 @@ export interface ShadowsocksConfig { | |||
password?: string; | |||
method?: string; | |||
name?: string; | |||
extra?: {[key: string]: string}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove this. See my comments on the storage format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in bc68e17. We now preserve the original access key and inject it into OutlineServer
.
src/www/app/outline_server.ts
Outdated
for (const serverId in configByAccessKey) { | ||
if (configByAccessKey.hasOwnProperty(serverId)) { | ||
const accessKey = configByAccessKey[serverId]; | ||
for (const serverId in configById) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to change, but I think for (const serverId of Object.keys(configById))
is the right way to do this. It removes the need for the hasOwnProperty below, I believe.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 3e18673.
src/www/app/outline_server.ts
Outdated
} | ||
} | ||
|
||
private loadServer(serverId: string, accessKey: string, config: ShadowsocksConfig) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change loadServer to take one parameter: OutlineServerJson.
It will simplify the loadServer methods.
Also, if you can't load the server from the OutlineServerJson, something is wrong. That will make it clearer what is needed to load a server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 3e18673.
src/www/app/outline_server.ts
Outdated
@@ -31,8 +31,9 @@ export class OutlineServer implements Server { | |||
errorMessageId?: string; | |||
|
|||
constructor( | |||
public readonly id: string, private readonly config: ShadowsocksConfig, | |||
private tunnel: Tunnel, private eventQueue: events.EventQueue) { | |||
public readonly id: string, public readonly accessKey: string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's confusing to have both the accessKey and the config.
Can't we have the same paramters as OutlineServerJson?
Pass id, name, and accessKey (besides tunnel and eventQueue).
We need to change the server factory accordingly too.
I think we can ditch ShadowsocksConfig for the most part, and have something specific for the proxying.
We have a design problem with the factory. We should eventually get rid of that, this code should control that. Instead we could be injecting a tunnel factory instead, but that a bit of a bigger change we can do later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 3e18673. I updated the server factory and OutlineServer
constructor per your suggestion. I think it's clearer now, at the expense of parsing the access key twice, since the server repository needs to flag unsupported ciphers for backwards compatibility.
I kept the serve name getter/setter in order to update config.name
, which is used by native code.
We have a design problem with the factory.
I have upcoming changes that will refactor the server factory into an interface that will allow native platforms to create only the tunnel.
The motivation behind this is to lay the groundwork for a single persistence format in the context of dynamic access keys (online config):
{accessKey, name}
, fromShadowsocksConfig
.Tunnel
interface not to own aShadowsocksConfig
.ServerConfig
,PersitentServer
andPersitentServerFactory
.ShadowsocksConfig
definition out ofmodel
.