1
0

Compare commits

...

168 Commits

Author SHA1 Message Date
Jorik Schellekens dbbdf1a4a9 Noop config if none is set 2019-09-06 10:25:55 +01:00
Jorik Schellekens 2f360de3b6 Key state is set in servename 2019-09-05 11:57:25 +01:00
Jorik Schellekens d934837b48 Fix port in use 2019-09-05 11:48:23 +01:00
Jorik Schellekens 5d02267d85 Priv ports not working 2019-09-05 11:43:38 +01:00
Jorik Schellekens 1e5b073fd8 Remove debug output 2019-09-05 11:29:19 +01:00
Jorik Schellekens 0bc1f9c2d1 Database defaults 2019-09-05 10:55:31 +01:00
Jorik Schellekens af3af0c8e4 Just say the config has been set up. 2019-09-05 10:55:19 +01:00
Jorik Schellekens 4798dd1f6b Update README.rst 2019-08-28 18:08:29 +01:00
Jorik Schellekens cea9329e0b Black is still the new black 2019-08-28 18:05:39 +01:00
Jorik Schellekens 53b94eff58 Update state representation 2019-08-28 17:43:35 +01:00
Jorik Schellekens 997fe30087 Handle synapse start correctly 2019-08-28 17:41:16 +01:00
Jorik Schellekens b18761b3e3 Handle synapse start failure correctly 2019-08-28 17:34:24 +01:00
Jorik Schellekens d56e1bedad Generate extra files needed for startup like the config file. 2019-08-28 16:59:48 +01:00
Jorik Schellekens b1c9c5a078 Fix config in use check 2019-08-28 16:31:12 +01:00
Jorik Schellekens d4a52d31ef Extra support for configuring postgres 2019-08-28 15:59:54 +01:00
Jorik Schellekens ec743b9165 Mk data_dir if it doesn't exist 2019-08-28 15:59:54 +01:00
Jorik Schellekens 6472d30e6a Disable going back when setup is finished 2019-08-28 15:59:54 +01:00
Jorik Schellekens 0cfe4b3db9 Formatting 2019-08-28 15:59:54 +01:00
Jorik Schellekens 0a2c2086c8 Include a done screen 2019-08-28 15:59:54 +01:00
Jorik Schellekens 406a790cf5 Handle errors when synapse fails to start. 2019-08-28 15:59:54 +01:00
Jorik Schellekens eba2753781 Server throws errors on synctl error 2019-08-28 15:59:54 +01:00
Jorik Schellekens 27135d9a28 Input validation on servername 2019-08-28 15:59:54 +01:00
Jorik Schellekens 127e0b1cd7 Use the write method. 2019-08-28 15:59:54 +01:00
Jorik Schellekens 2b08f5c6bd Error reporting for cert paths 2019-08-28 15:59:54 +01:00
Jorik Schellekens 8e1d22c6f7 Remove option to upload tls certs 2019-08-28 15:59:54 +01:00
Jorik Schellekens 2d56f81d05 We need to check that the pasth is abs and we don't need to validate 2019-08-28 15:59:54 +01:00
Jorik Schellekens c36c0278b0 Not importing that from there 2019-08-28 15:59:54 +01:00
Jorik Schellekens 66bc80eddf Remove an endpoint that never existed 2019-08-28 15:59:54 +01:00
Jorik Schellekens 01d05e40a9 Remove use of servername endpoint 2019-08-28 15:59:54 +01:00
Jorik Schellekens 1cef415af7 Fix validation wrapper to work for oop.
This is not general enough to be honest.
2019-08-28 15:59:54 +01:00
Jorik Schellekens 13ea03ccd6 Cleanup and use new model 2019-08-28 15:59:54 +01:00
Jorik Schellekens ea54afb1c3 some better routing 2019-08-28 15:59:54 +01:00
Jorik Schellekens 39f758c07c It's an OO language. Use it like one. 2019-08-28 15:59:54 +01:00
Jorik Schellekens 0690f7fc55 When I'm alone I count myslef. 2019-08-28 15:59:54 +01:00
Jorik Schellekens d63a58462f Make the model stateless. 2019-08-28 15:59:54 +01:00
Jorik Schellekens e5622a483c We're not using these 2019-08-28 15:59:54 +01:00
Jorik Schellekens ec76921ecb We're not doing the whole subconfig thing anymore. 2019-08-28 15:59:54 +01:00
Jorik Schellekens bbd4880371 Link front and backend to start synapse 2019-08-28 15:59:54 +01:00
Jorik Schellekens 0d73df5c10 Print something if the unforseen happens. 2019-08-28 15:59:54 +01:00
Jorik Schellekens b78ae32b4e Fix config generation 2019-08-28 15:59:54 +01:00
Jorik Schellekens 763c1d320c Remove unused api endpoints. 2019-08-28 15:59:54 +01:00
Jorik Schellekens 0a5099cd24 Write out the configs. 2019-08-28 15:59:54 +01:00
Jorik Schellekens 932aeca8c6 Quick cleanup! 2019-08-28 15:59:54 +01:00
Jorik Schellekens e58f3ffe2f Use the nice new api 2019-08-28 15:59:54 +01:00
Jorik Schellekens 4b72b03d00 Don't need to specify those 2019-08-28 15:59:54 +01:00
Jorik Schellekens 69d0da4e32 Move args to generate_config 2019-08-28 15:59:54 +01:00
Jorik Schellekens ac47193ff0 Produce the remaining config 2019-08-28 15:59:54 +01:00
Jorik Schellekens aed01c8b93 Tls config 2019-08-28 15:59:54 +01:00
Jorik Schellekens 84d04e9001 Don't need those either 2019-08-28 15:59:54 +01:00
Jorik Schellekens d79788c953 Won't be needing those 2019-08-28 15:59:54 +01:00
Jorik Schellekens 22fcbbfe5e Started templating the output 2019-08-28 15:59:54 +01:00
Jorik Schellekens e48e9ba41b How was this even remotely working? 2019-08-28 15:59:54 +01:00
Jorik Schellekens 438d7fa75a Fix config tls path bug 2019-08-28 15:59:54 +01:00
Jorik Schellekens 2f04eb2797 Fix port settings 2019-08-28 15:59:54 +01:00
Nad Chishtie 4ac5bd96e8 Tweak margin 2019-08-28 15:59:54 +01:00
Nad Chishtie e023fd563f Add missing space 2019-08-28 15:59:54 +01:00
Nad Chishtie 70c04c54b1 Compile 2019-08-28 15:59:54 +01:00
Nad Chishtie 4d2f0090e9 Simplify select styles to fix cross browser bugs 2019-08-28 15:59:54 +01:00
Nad Chishtie cc8fa83fcc Separate select & input @mixins 2019-08-28 15:59:54 +01:00
Jorik Schellekens da19eb18be Fix database configuration 2019-08-28 15:59:54 +01:00
Nad Chishtie 58802ef961 Reorder & clean up CSS, remove extraneous classes and properties 2019-08-28 15:59:54 +01:00
Nad Chishtie 1629d91b68 Remove redundant comments 2019-08-28 15:59:54 +01:00
Nad Chishtie 9b0fc37f2d Use a sane system font stack 2019-08-28 15:59:54 +01:00
Nad Chishtie 9349614a16 Various WIP CSS tweaks 2019-08-28 15:59:54 +01:00
Nad Chishtie 20eb0de9b5 Polish delegation spacing 2019-08-28 15:59:54 +01:00
Nad Chishtie 4fe0c9eb1c Polish titles in setup complete 2019-08-28 15:59:54 +01:00
Nad Chishtie 82008faff8 Polish input selection 2019-08-28 15:59:54 +01:00
Nad Chishtie 02dc1cf13a Polish buttons that relate to input 2019-08-28 15:59:54 +01:00
Jorik Schellekens 45557de936 Weird indent 2019-08-28 15:59:54 +01:00
Jorik Schellekens 4bd7d5fa26 Disable continue in setup section 2019-08-28 15:59:54 +01:00
Jorik Schellekens ab805cd9ad Redundent whitespace 2019-08-28 15:59:54 +01:00
Jorik Schellekens d48a316a55 Synapse port bug 2019-08-28 15:59:54 +01:00
Jorik Schellekens d8a19849e8 Clickable 2019-08-28 15:59:54 +01:00
Jorik Schellekens ff59e6a85c Some little ui bugs 2019-08-28 15:59:54 +01:00
Jorik Schellekens 8b2acd1454 So much lint in my life 2019-08-28 15:59:54 +01:00
Jorik Schellekens a35dcd33b1 Open servername ui by default 2019-08-28 15:59:54 +01:00
Jorik Schellekens f56760604d Chevron 2019-08-28 15:59:54 +01:00
Jorik Schellekens 089645458f Only proceed if you've copied/downloaded 2019-08-28 15:59:54 +01:00
Jorik Schellekens 3f8100bda2 Llittle lin 2019-08-28 15:59:54 +01:00
Jorik Schellekens 6afcf0fc92 Nicer TLS button text 2019-08-28 15:59:54 +01:00
Jorik Schellekens 9e7a889418 Fix complete setup bugs 2019-08-28 15:59:54 +01:00
Jorik Schellekens ed3e3df538 Display port errors inline 2019-08-28 15:59:54 +01:00
Jorik Schellekens eb537f087f Linting. Unfortunately we're using 4 spaces 2019-08-28 15:59:54 +01:00
Jorik Schellekens e0452b89f2 Just a little lint 2019-08-28 15:59:54 +01:00
Jorik Schellekens a26e54860b Smoothing out the UI intro 2019-08-28 15:59:54 +01:00
Jorik Schellekens d01310d9e9 This removes a bug that would otherwise have happened
... in the future o.0....
2019-08-28 15:59:54 +01:00
Jorik Schellekens 6b0e658440 Get rid of annoying warning and unused prop 2019-08-28 15:59:54 +01:00
Jorik Schellekens e03421b050 Nicer port text 2019-08-28 15:59:54 +01:00
Jorik Schellekens e79d63c88d Cursor indications 2019-08-28 15:59:54 +01:00
Jorik Schellekens 2440c70630 Fix Delegation skip 2019-08-28 15:59:54 +01:00
Jorik Schellekens 25f8fba5a3 Setting port bug 2019-08-28 15:59:54 +01:00
Jorik Schellekens bea99ee6a1 Move port entry into tab 2019-08-28 15:59:54 +01:00
Jorik Schellekens 96778c68d6 Implement state resets when going back. 2019-08-28 15:59:54 +01:00
Jorik Schellekens 53fbf82f4e Remove useless console.logs 2019-08-28 15:59:54 +01:00
Jorik Schellekens 546ce01e0d Same console error 2019-08-28 15:59:54 +01:00
Jorik Schellekens 6bc3ff60c7 Fix errormessage in console. 2019-08-28 15:59:54 +01:00
Nad Chishtie 511f8b626b Add WIP CSS classes 2019-08-28 15:59:54 +01:00
Nad Chishtie 53ecf93da1 First cut of visual polish for installer. 2019-08-28 15:59:54 +01:00
Jorik Schellekens aa657705e4 Lint scss 2019-08-28 15:59:54 +01:00
Jorik Schellekens 659471703f I hope this doesn't break your design Nad. 2019-08-28 15:59:54 +01:00
Jorik Schellekens d6c296d184 Love a little less lint 2019-08-28 15:59:54 +01:00
Jorik Schellekens 5414d470f5 Linted and loaded. 2019-08-28 15:59:54 +01:00
Jorik Schellekens e34448600e Ignore yarn errors 2019-08-28 15:59:54 +01:00
Jorik Schellekens 153a340a6a Ignore config_dir 2019-08-28 15:59:54 +01:00
Jorik Schellekens 3cfe24a32c setup linting. 2019-08-28 15:59:54 +01:00
Jorik Schellekens c747fca2e8 Add some structure. 2019-08-28 15:59:54 +01:00
Jorik Schellekens ce2b433bf7 Where did this come from? 2019-08-28 15:59:53 +01:00
Jorik Schellekens 07fe7906ad Highlight invalid ports. 2019-08-28 15:59:53 +01:00
Jorik Schellekens f618f8a44f Move webui to top level 2019-08-28 15:59:53 +01:00
Jorik Schellekens 893a346212 Remove cli controller 2019-08-28 15:59:53 +01:00
Jorik Schellekens 23a837c194 Complete all toggle actions. 2019-08-28 15:59:53 +01:00
Jorik Schellekens a41a655819 Set report_stats to false by default. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 2c7a57f9d9 Few more toggles. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 2d9f82e18a Toggle next 2019-08-28 15:59:53 +01:00
Jorik Schellekens 58964e4063 Move to scss. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 62e45eebbf Toggle a bit better. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 2a909302cf Cardify setup completion. 2019-08-28 15:59:53 +01:00
Jorik Schellekens a5fa7653fa Cardify Database 2019-08-28 15:59:53 +01:00
Jorik Schellekens b6a8d989f4 Cardify port selection 2019-08-28 15:59:53 +01:00
Jorik Schellekens a209536849 Remove going back logic for the time being. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 442da5fbdd TLS accordion entry 2019-08-28 15:59:53 +01:00
Jorik Schellekens 353543a780 Accordion stats, keys and delegation. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 6744c080d7 Changes in structure. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 63b9c8d3a6 Start moving to accordion. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 388dcb1e6c Missed some arguments. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 280d50cd4f Forgot to save 2019-08-28 15:59:53 +01:00
Jorik Schellekens 47ca664311 Missing import 2019-08-28 15:59:53 +01:00
Jorik Schellekens 4573aceeeb Restructure state. 2019-08-28 15:59:53 +01:00
Jorik Schellekens c9c7960390 Refactoring the reducers as prep for accordian style 2019-08-28 15:59:53 +01:00
Jorik Schellekens 7e8f993a84 Some refactoring 2019-08-28 15:59:53 +01:00
Jorik Schellekens 83198702f5 Clean up debug statements 2019-08-28 15:59:53 +01:00
Jorik Schellekens e00008186b Setup the secret key and start synapse. 2019-08-28 15:59:53 +01:00
Jorik Schellekens fb592b6c98 Use python instead 2019-08-28 15:59:53 +01:00
Jorik Schellekens 1c9c8186b1 First try at starting synapse 2019-08-28 15:59:53 +01:00
Jorik Schellekens c5ca02b3d0 Write out the yaml to synapse 2019-08-28 15:59:53 +01:00
Jorik Schellekens eb10b04865 Relative paths. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 39641482ef My reflex is to write markdown. I forgot this was RST 2019-08-28 15:59:53 +01:00
Jorik Schellekens eedd86766d Handle relative paths correctly! 2019-08-28 15:59:53 +01:00
Jorik Schellekens 3a8bebff88 That shouldn't be tracked. 2019-08-28 15:59:53 +01:00
Jorik Schellekens c2bf291a0d Yaml output. 2019-08-28 15:59:53 +01:00
Jorik Schellekens a8cc43dfac Finished templates, database config, and started converting options to synapse yaml. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 3ae448a183 Templates 2019-08-28 15:59:53 +01:00
Jorik Schellekens b87c85a87d Startup instructions. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 3a9194e652 Port verification endpoint 2019-08-28 15:59:53 +01:00
Jorik Schellekens 3b3abcc273 Port selection 2019-08-28 15:59:53 +01:00
Jorik Schellekens d599ab6249 Matrix branding 2019-08-28 15:59:53 +01:00
Jorik Schellekens 96c56177a8 I think that title makes more sense. 2019-08-28 15:59:53 +01:00
Jorik Schellekens a079c9bf0d UI for port selection. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 7fe1148f44 'not useing' tls is no longer an option. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 02845e7e3a Selecting ports for delegation. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 9a0ba7696e None is no longer a valid reverse proxy option. 2019-08-28 15:59:53 +01:00
Jorik Schellekens bb79dc1351 Missing import 2019-08-28 15:59:53 +01:00
Jorik Schellekens 2835f033b4 Delegation port selection. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 1481108afa Reverse proxy explenations. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 318b66fa2a Bad hack to make things format correctly 2019-08-28 15:59:53 +01:00
Jorik Schellekens b5881cab45 Fix text for tlx and remove dud component. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 175ee86e6d Present the Reverse Proxy choice as a TLS config option. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 3242aa9af2 TLS ACME etc 2019-08-28 15:59:53 +01:00
Jorik Schellekens 9280882cb9 Cert endpoints. 2019-08-28 15:59:53 +01:00
Jorik Schellekens cd0cb18d94 more UI 2019-08-28 15:59:53 +01:00
Jorik Schellekens e5765ac4af Consolidated servers to avoid CORS. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 2a34815d48 Add basic flow control 2019-08-28 15:59:53 +01:00
Jorik Schellekens ced3cb281a Add endpoint to check if server has been setup. 2019-08-28 15:59:53 +01:00
Jorik Schellekens a00ce82e46 Some not too helpful docs. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 3cf68de825 Startup script 2019-08-28 15:59:53 +01:00
Jorik Schellekens d7a8670cf5 Represent synapses config setup as a data model. 2019-08-28 15:59:53 +01:00
Jorik Schellekens 4163c25449 Set up fronted as a react project. 2019-08-28 15:59:53 +01:00
Jorik Schellekens d2d36126f7 Set up initial endpoints for backend server. 2019-08-28 15:59:53 +01:00
88 changed files with 5326 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
config_dir
+40
View File
@@ -0,0 +1,40 @@
#! python
import argparse
import os.path as path
import sys
from synapse_topology.server import Server
from synapse_topology.model import Model
from twisted.internet import endpoints, reactor
from twisted.web.server import Site
from twisted.logger import (
eventsFromJSONLogFile,
textFileLogObserver,
globalLogPublisher,
)
globalLogPublisher.addObserver(textFileLogObserver(sys.stdout))
parser = argparse.ArgumentParser(description="Synapse configuration util")
parser.add_argument(
"config_dir",
metavar="CONFIG_DIR",
type=str,
help="Path the directory containing synapse's configuration files.",
)
args = parser.parse_args()
if not path.isdir(args.config_dir):
print("'{}' is not a directory.".format(args.config_dir))
exit(1)
model = Model(args.config_dir)
server = Server(model)
server.app.run("localhost", 8888)
+18
View File
@@ -0,0 +1,18 @@
Backend
=======
::
Make sure you have synapse and klein installed in your pip env
```
./__init__.py config_dir
```
Frontend
========
Start the Backend and then
.. code:: bash
cd view/webui
yarn install
yarn watch
+95
View File
@@ -0,0 +1,95 @@
import yaml
import subprocess
from os import mkdir
from os.path import abspath, join, exists, isdir
from synapse.config.homeserver import HomeServerConfig
from .constants import CONFIG_LOCK, CONFIG_LOCK_DATA, DATA_SUBDIR, SERVER_NAME
from .errors import BaseConfigInUseError, ConfigNotFoundError, ServernameNotSetError
from .config import create_config
from .util import is_subpath
from synapse.config import find_config_files
class Model:
"""
The Model brokers modification of the config file and signing keys in the config
directory.
"""
def __init__(self, config_dir):
self.config_dir = abspath(config_dir)
self.data_dir = abspath(join(self.config_dir, DATA_SUBDIR))
if not isdir(self.config_dir):
mkdir(self.config_dir)
if not isdir(self.data_dir):
mkdir(self.data_dir)
def get_config(self, config_path):
"""
Retrieves a config from the config directory. Any path can be provided
but it must be a subdirectory of self.config_dir
Args:
config_path (str): path to the config
Returns:
dict: the yaml parse of the config file
"""
conf_path = abspath(join(self.config_dir, config_path))
if not is_subpath(self.config_dir, conf_path):
raise FileNotFoundError()
with open(conf_path, "r") as f:
return yaml.safe_load(f)
def write_config(self, config):
"""
Given a config generates a templated config from synapse and writes it
out to the config dir. It will raise an exception if the config in
the config directory is in use.
Args:
config (dict): The configuration to template out.
"""
if self.config_in_use():
raise BaseConfigInUseError()
for conf_name, conf in create_config(
self.config_dir, self.data_dir, config
).items():
with open(abspath(join(self.config_dir, conf_name)), "w") as f:
f.write(conf)
def config_in_use(self):
"""
Checks if we set whether the config is in use. If it was set up by the system
but synapse wasn't launched yet we will have set this to False. However if
it's not present we assume someone else has set up synapse before so we assume
the config is in use.
"""
config = {}
config_files = find_config_files([self.config_dir])
for config_file in config_files:
with open(config_file) as stream:
config.update(yaml.safe_load(stream))
if not config:
return False
print(config.get(CONFIG_LOCK))
return config.get(CONFIG_LOCK, True)
def generate_secret_key(self, server_name):
if self.config_in_use():
raise BaseConfigInUseError()
signing_key_path = join(self.config_dir, server_name + ".signing.key")
subprocess.run(["generate_signing_key.py", "-o", signing_key_path])
with open(signing_key_path, "r") as f:
return f.read()
+66
View File
@@ -0,0 +1,66 @@
from os.path import join
import yaml
from synapse.config.database import DatabaseConfig
from synapse.config.homeserver import HomeServerConfig
from synapse.config.logger import LoggingConfig
from synapse.config.server import ServerConfig
from synapse.config.tls import TlsConfig
def create_config(config_dir_path, data_dir_path, conf):
server_name = conf["server_name"]
del conf["server_name"]
server_config_in_use = conf["server_config_in_use"]
del conf["server_config_in_use"]
database_conf = conf["database"]
del conf["database"]
if database_conf["name"] == "sqlite3":
database_conf.setdefault(
"args", {"database": join(data_dir_path, "homeserver.db")}
)
base_configs = [ServerConfig, DatabaseConfig, TlsConfig]
# Generate configs for all the ones we didn't cover explicitely
uninitialized_configs = [
x for x in list(HomeServerConfig.__bases__) if x not in base_configs
]
class BaseConfig(*base_configs):
pass
class AdvancedConfig(*uninitialized_configs):
pass
config_args = {
"config_dir_path": config_dir_path,
"data_dir_path": data_dir_path,
"server_name": server_name,
**conf,
"database_conf": database_conf,
"generate_secrets": True,
}
base_config = BaseConfig()
advanced_config = AdvancedConfig()
base_config_text = base_config.generate_config(**config_args)
advanced_config_text = advanced_config.generate_config(**config_args)
config = {}
config.update(yaml.safe_load(base_config_text))
config.update(yaml.safe_load(advanced_config_text))
base_config.generate_missing_files(config, config_dir_path)
advanced_config.generate_missing_files(config, config_dir_path)
return {
"homeserver_basic_config.yaml": base_config_text
+ "\n\nserver_config_in_use: {}".format(server_config_in_use),
"homeserver_advanced_config.yaml": advanced_config_text,
}
+22
View File
@@ -0,0 +1,22 @@
# Paths
DATA_SUBDIR = "data"
# Config options
SERVER_NAME = "server_name"
CONFIG_LOCK = "server_config_in_use"
SECRET_KEY = "macaroon_secret_key"
CONFIG_LOCK_DATA = """
## CONFIG LOCK ##
# Specifies whether synapse has been started with this config.
# If set to True the setup util will not go through the initialization
# phase which sets the server name and server keys.
{}: {{}}
""".format(
CONFIG_LOCK
)
+14
View File
@@ -0,0 +1,14 @@
class ConfigNotFoundError(FileNotFoundError):
def __init__(self, config_name):
self.config_name = config_name
def get_config_name(self):
return self.config_name
class ServernameNotSetError(Exception):
pass
class BaseConfigInUseError(Exception):
pass
+8
View File
@@ -0,0 +1,8 @@
from os.path import realpath, pardir, sep, relpath
def is_subpath(superpath, subpath):
subpath = realpath(subpath)
superpath = realpath(superpath)
relative = relpath(subpath, superpath)
return not relative.startswith(pardir + sep)
+3
View File
@@ -0,0 +1,3 @@
# from .error_handlers import ErrorHandledServer as Server
from .error_handlers import ErrorHandledServer as Server
View File
+47
View File
@@ -0,0 +1,47 @@
from jsonschema import ValidationError
from simplejson.errors import JSONDecodeError
from synapse_topology.model.errors import (
BaseConfigInUseError,
ConfigNotFoundError,
ServernameNotSetError,
)
from .server import Server
class ErrorHandledServer(Server):
app = Server.app
@app.handle_errors(ValidationError)
def validation_error(self, request, failure):
request.setResponseCode(400)
print("Invalid post schema {}".format(failure.getErrorMessage()))
return "Invalid post schema {}".format(failure.getErrorMessage())
@app.handle_errors(JSONDecodeError)
def json_decode_error(self, request, failure):
request.setResponseCode(400)
return "Invalid post json"
@app.handle_errors(ServernameNotSetError)
def not_initialised(self, request, failure):
request.setResponseCode(500)
return (
"Config file not setup, please initialise it using the /servername endpoint"
)
@app.handle_errors(ConfigNotFoundError)
def config_not_found(self, request, failure):
request.setResponseCode(404)
return "The config does not exist"
@app.handle_errors(BaseConfigInUseError)
def base_config_in_use(self, request, failure):
request.setResponseCode(409)
return "Sever name and keys already configured"
@app.handle_errors(Exception)
def handle_generic_error(self, request, failure):
print(failure)
request.setResponseCode(500)
return "Internal server error\n{}".format(failure)
+55
View File
@@ -0,0 +1,55 @@
BASE_CONFIG_SCHEMA = {
"type": "object",
"properties": {
"server_name": {"type": "string", "minlength": 1},
"report_stats": {"type": "boolean"},
"log_config": {"type": "string", "minlength": 1},
"media_store_path": {"type": "string", "minlength": 1},
"uploads_path": {"type": "string", "minlength": 1},
"pid_file": {"type": "string", "minlength": 1},
"listeners": {"type": "array"},
"acme": {"type": "object"},
"database": {
"type": "object",
"properties": {
"name": {"type": "string", "minlength": 1},
"args": {"type": "object"},
},
"required": ["name"],
},
"tls_certificate_path": {"type": "string", "minlength": 1},
"tls_private_key_path": {"type": "string", "minlength": 1},
"server_config_in_use": {"type": "boolean"},
},
"required": ["server_name", "report_stats", "database"],
}
CERT_PATHS_SCHEMA = {
"type": "object",
"properties": {
"cert_path": {"type": "string", "minlength": 1},
"cert_key_path": {"type": "string", "minlength": 1},
},
"required": ["cert_path", "cert_key_path"],
}
CERTS_SCHEMA = {
"type": "object",
"properties": {
"cert": {"type": "string", "minlength": 1},
"cert_key": {"type": "string", "minlength": 1},
},
"required": ["cert", "cert_key"],
}
PORTS_SCHEMA = {
"type": "object",
"properties": {"ports": {"type": "array"}},
"required": ["ports"],
}
SECRET_KEY_SCHEMA = {
"type": "object",
"properties": {"server_name": {"type": "string", "minlength": "1"}},
"required": ["server_name"],
}
+99
View File
@@ -0,0 +1,99 @@
from os.path import abspath, dirname, join, isabs
from canonicaljson import json
from twisted.web.static import File
from klein import Klein
from .utils import port_checker
from synapse_topology.model import constants
from .schemas import (
BASE_CONFIG_SCHEMA,
CERT_PATHS_SCHEMA,
CERTS_SCHEMA,
PORTS_SCHEMA,
SECRET_KEY_SCHEMA,
)
from .utils import validate_schema, log_body_if_fail
import subprocess
import sys
class Server:
app = Klein()
def __init__(self, model):
self.model = model
def server_webui(self, request):
client_path = abspath(join(dirname(abspath(__file__)), "../webui/dist/"))
print(client_path)
return File(client_path)
app.route("/topology_webui", branch=True)(server_webui)
app.route("/topology_webui/", branch=True)(server_webui)
@app.route("/setup", methods=["GET"])
def get_config_setup(self, request):
return json.dumps(
{
constants.CONFIG_LOCK: self.model.config_in_use(),
"config_dir": self.model.config_dir,
}
)
@app.route("/secretkey", methods=["POST"])
@validate_schema(SECRET_KEY_SCHEMA)
def get_secret_key(self, request, body):
return json.dumps(
{"secret_key": self.model.generate_secret_key(body["server_name"])}
)
@app.route("/config", methods=["GET"])
def get_config(self, request):
return str(self.model.get_config())
@app.route("/config", methods=["POST"])
@validate_schema(BASE_CONFIG_SCHEMA)
def set_config(self, request, body):
self.model.write_config(body)
@app.route("/testcertpaths", methods=["POST"])
def test_cert_paths(self, request):
body = json.loads(request.content.read())
result = {}
config_path = self.model.config_dir
for name, path in body.items():
if not isabs(path):
path = abspath(join(config_path, path))
try:
with open(path, "r"):
result[name] = {"invalid": False, "absolute_path": path}
except:
result[name] = {"invalid": True}
return json.dumps(result)
@app.route("/certs", methods=["POST"])
@validate_schema(CERTS_SCHEMA)
def upload_certs(self, request, body):
self.model.add_certs(**body)
@app.route("/ports", methods=["POST"])
@validate_schema(PORTS_SCHEMA)
def check_ports(self, request, body):
results = []
for port in body["ports"]:
results.append(port_checker(port))
return json.dumps({"ports": results})
@app.route("/start", methods=["POST"])
def start_synapse(self, request):
print("Starting synapse")
subprocess.check_output(["synctl", "start", self.model.config_dir])
@app.route("/favicon.ico")
def noop(self, request):
return
+47
View File
@@ -0,0 +1,47 @@
from functools import wraps
from canonicaljson import json
from jsonschema import validate
from contextlib import closing
import socket
def validate_schema(schema):
def _wrap_validate(func):
@wraps(func)
def _do_validate(self, request):
body = json.loads(request.content.read())
print(body)
validate(instance=body, schema=schema)
return func(self, request, body)
return _do_validate
return _wrap_validate
def port_checker(port):
if port < 0 or 65535 < port:
return False
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
try:
sock.bind(("0.0.0.0", port))
sock.listen()
return True
except:
return False
def log_body_if_fail(func):
@wraps(func)
def _log_wrapper(self, request):
try:
return func(self, request)
except Exception:
body = json.loads(request.content.read())
print(body)
raise
return _log_wrapper
+9
View File
@@ -0,0 +1,9 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
],
"plugins": [
"@babel/plugin-proposal-object-rest-spread"
],
}
+131
View File
@@ -0,0 +1,131 @@
const path = require('path');
// get the path of the js-sdk so we can extend the config
// eslint supports loading extended configs by module,
// but only if they come from a module that starts with eslint-config-
// So we load the filename directly (and it could be in node_modules/
// or or ../node_modules/ etc)
module.exports = {
parser: "babel-eslint",
plugins: [
"react",
"babel"
],
env: {
es6: true,
},
parserOptions: {
ecmaFeatures: {
jsx: true,
}
},
rules: {
// rules we've always adhered to or now do
"max-len": ["error", {
code: 90,
ignoreComments: true,
}],
curly: ["error", "multi-line"],
"prefer-const": ["error"],
"comma-dangle": ["error", {
arrays: "always-multiline",
objects: "always-multiline",
imports: "always-multiline",
exports: "always-multiline",
functions: "always-multiline",
}],
// loosen jsdoc requirements a little
"require-jsdoc": ["error", {
require: {
FunctionDeclaration: false,
}
}],
"valid-jsdoc": ["error", {
requireParamDescription: false,
requireReturn: false,
requireReturnDescription: false,
}],
// rules we do not want from eslint-recommended
"no-console": ["off"],
"no-constant-condition": ["off"],
"no-empty": ["error", { "allowEmptyCatch": true }],
// rules we do not want from the google styleguide
"object-curly-spacing": ["off"],
"spaced-comment": ["off"],
"guard-for-in": ["off"],
// in principle we prefer single quotes, but life is too short
quotes: ["off"],
// rules we'd ideally like to adhere to, but the current
// code does not (in most cases because it's still ES5)
// we set these to warnings, and assert that the number
// of warnings doesn't exceed a given threshold
"no-var": ["warn"],
"brace-style": ["warn", "1tbs", { "allowSingleLine": true }],
"prefer-rest-params": ["warn"],
"prefer-spread": ["warn"],
"padded-blocks": ["warn"],
"no-extend-native": ["warn"],
"camelcase": ["warn"],
"no-multi-spaces": ["error", { "ignoreEOLComments": true }],
"space-before-function-paren": ["error", {
"anonymous": "never",
"named": "never",
"asyncArrow": "always",
}],
"arrow-parens": "off",
// eslint's built in no-invalid-this rule breaks with class properties
"no-invalid-this": "off",
// so we replace it with a version that is class property aware
"babel/no-invalid-this": "error",
// We appear to follow this most of the time, so let's enforce it instead
// of occasionally following it (or catching it in review)
"keyword-spacing": "error",
/** react **/
// This just uses the react plugin to help eslint known when
// variables have been used in JSX
"react/jsx-uses-vars": "error",
// Don't mark React as unused if we're using JSX
"react/jsx-uses-react": "error",
// bind or arrow function in props causes performance issues
// (but we currently use them in some places)
// It's disabled here, but we should using it sparingly.
"react/jsx-no-bind": "off",
"react/jsx-key": ["error"],
// Components in JSX should always be defined.
"react/jsx-no-undef": "error",
// Assert no spacing in JSX curly brackets
// <Element prop={ consideredError} prop={notConsideredError} />
//
// https://github.com/yannickcr/eslint-plugin-react/blob/HEAD/docs/rules/jsx-curly-spacing.md
//
// Disabled for now - if anything we'd like to *enforce* spacing in JSX
// curly brackets for legibility, but in practice it's not clear that the
// consistency particularly improves legibility here. --Matthew
//
// "react/jsx-curly-spacing": ["error", {"when": "never", "children": {"when": "always"}}],
// Assert spacing before self-closing JSX tags, and no spacing before or
// after the closing slash, and no spacing after the opening bracket of
// the opening tag or closing tag.
//
// https://github.com/yannickcr/eslint-plugin-react/blob/HEAD/docs/rules/jsx-tag-spacing.md
"react/jsx-tag-spacing": ["error"],
},
settings: {
flowtype: {
onlyFilesWithFlowAnnotation: true
},
},
};
+3
View File
@@ -0,0 +1,3 @@
node_modules
yarn-error.log
dist
+23
View File
@@ -0,0 +1,23 @@
module.exports = {
"extends": "stylelint-config-standard",
"plugins": [
"stylelint-scss",
],
"rules": {
"indentation": 4,
"comment-empty-line-before": null,
"declaration-empty-line-before": null,
"length-zero-no-unit": null,
"rule-empty-line-before": null,
"color-hex-length": null,
"max-empty-lines": null,
"number-no-trailing-zeros": null,
"number-leading-zero": null,
"selector-list-comma-newline-after": null,
"at-rule-no-unknown": null,
"scss/at-rule-no-unknown": [true, {
// https://github.com/vector-im/riot-web/issues/10544
"ignoreAtRules": ["define-mixin"],
}],
}
}
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
{
"name": "synapse_topology_webui",
"version": "0.0.0",
"description": "A simple webui for initialising the synapse startup",
"main": "index.js",
"author": "Jorik Schellekens (matrix.org)",
"license": "Apache-2.0",
"private": true,
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/node": "^7.5.5",
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.5.5",
"babel-eslint": "^10.0.2",
"babel-loader": "^8.0.6",
"css-loader": "^3.2.0",
"eslint": "^6.1.0",
"eslint-plugin-babel": "^5.3.0",
"eslint-plugin-react": "^7.14.3",
"file-loader": "^4.1.0",
"html-webpack-plugin": "^3.2.0",
"html-webpack-tags-plugin": "^2.0.13",
"less": "^3.9.0",
"node-sass": "^4.12.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"redux-devtools-extension": "^2.13.8",
"sass-loader": "^7.2.0",
"style-loader": "^0.23.1",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2"
},
"scripts": {
"build": "webpack -p --progress --config webpack.config.babel.js",
"dev-build": "webpack --progress -d --config webpack.config.babel.js",
"watch": "webpack --progress -d --config webpack.config.babel.js --watch"
},
"dependencies": {
"fetch-absolute": "^1.0.0",
"react-bootstrap": "^1.0.0-beta.11",
"react-icons": "^3.7.0",
"react-localize-redux": "^3.5.3",
"react-redux": "^7.1.0",
"redux": "^4.0.4",
"redux-thunk": "^2.3.0",
"yaml": "^1.6.0"
}
}
@@ -0,0 +1,46 @@
Digitized data copyright (c) 2010 Google Corporation
with Reserved Font Arimo, Tinos and Cousine.
Copyright (c) 2012 Red Hat, Inc.
with Reserved Font Name Liberation.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -0,0 +1,5 @@
<svg width="75" height="32" xmlns="http://www.w3.org/2000/svg">
<g fill="#2D2D2D" fillRule="nonzero">
<path d="M.936.732V31.25H3.13v.732H.095V0h3.034v.732zM9.386 10.407v1.544h.044a4.461 4.461 0 0 1 1.487-1.368c.58-.323 1.245-.485 1.993-.485.72 0 1.377.14 1.972.42.595.279 1.047.771 1.355 1.477.338-.5.796-.941 1.377-1.323.58-.383 1.266-.574 2.06-.574.602 0 1.16.074 1.674.22.514.148.954.383 1.322.707.366.323.653.746.859 1.268.205.522.308 1.15.308 1.887v7.633H20.71v-6.464c0-.383-.015-.743-.044-1.082a2.305 2.305 0 0 0-.242-.882 1.473 1.473 0 0 0-.584-.596c-.257-.146-.606-.22-1.047-.22-.44 0-.796.085-1.068.253-.272.17-.485.39-.639.662a2.654 2.654 0 0 0-.308.927 7.074 7.074 0 0 0-.078 1.048v6.354h-3.128v-6.398c0-.338-.007-.673-.021-1.004a2.825 2.825 0 0 0-.188-.916 1.411 1.411 0 0 0-.55-.673c-.258-.168-.636-.253-1.135-.253a2.33 2.33 0 0 0-.584.1 1.94 1.94 0 0 0-.705.374c-.228.184-.422.449-.584.794-.161.346-.242.798-.242 1.357v6.619H6.434V10.407h2.952zM25.842 12.084a3.751 3.751 0 0 1 1.233-1.17 5.37 5.37 0 0 1 1.685-.629 9.579 9.579 0 0 1 1.884-.187c.573 0 1.153.04 1.74.121.588.081 1.124.24 1.609.475.484.235.88.562 1.19.981.308.42.462.975.462 1.666v5.934c0 .516.03 1.008.088 1.478.058.471.161.824.308 1.06H32.87a4.435 4.435 0 0 1-.22-1.104c-.5.515-1.087.876-1.762 1.081a7.084 7.084 0 0 1-2.071.31c-.544 0-1.05-.067-1.52-.2a3.472 3.472 0 0 1-1.234-.617 2.87 2.87 0 0 1-.826-1.059c-.199-.426-.298-.934-.298-1.522 0-.647.114-1.18.342-1.6.227-.419.52-.753.881-1.004.36-.25.771-.437 1.234-.562.462-.125.929-.224 1.399-.298.47-.073.932-.132 1.387-.176.456-.044.86-.11 1.212-.199.353-.088.631-.217.837-.386.206-.169.301-.415.287-.74 0-.337-.055-.606-.166-.804a1.217 1.217 0 0 0-.44-.464 1.737 1.737 0 0 0-.639-.22 5.292 5.292 0 0 0-.782-.055c-.617 0-1.101.132-1.454.397-.352.264-.558.706-.617 1.323h-3.128c.044-.735.227-1.345.55-1.83zm6.179 4.423a5.095 5.095 0 0 1-.639.165 9.68 9.68 0 0 1-.716.11c-.25.03-.5.067-.749.11a5.616 5.616 0 0 0-.694.177 2.057 2.057 0 0 0-.594.298c-.17.125-.305.284-.408.474-.103.192-.154.434-.154.728 0 .28.051.515.154.706.103.192.242.342.419.453.176.11.381.187.617.231.234.044.477.066.726.066.617 0 1.094-.102 1.432-.309.338-.205.587-.452.75-.739.16-.286.26-.576.297-.87.036-.295.055-.53.055-.707v-1.17a1.4 1.4 0 0 1-.496.277zM43.884 10.407v2.096h-2.291v5.647c0 .53.088.883.264 1.059.176.177.529.265 1.057.265.177 0 .345-.007.507-.022.161-.015.316-.037.463-.066v2.426a7.49 7.49 0 0 1-.882.089 21.67 21.67 0 0 1-.947.022c-.484 0-.944-.034-1.377-.1a3.233 3.233 0 0 1-1.145-.386 2.04 2.04 0 0 1-.782-.816c-.191-.353-.287-.816-.287-1.39v-6.728H36.57v-2.096h1.894v-3.42h3.129v3.42h2.29zM48.355 10.407v2.118h.044a3.907 3.907 0 0 1 1.454-1.754 4.213 4.213 0 0 1 1.036-.497 3.734 3.734 0 0 1 1.145-.176c.206 0 .433.037.683.11v2.912a5.862 5.862 0 0 0-.528-.077 5.566 5.566 0 0 0-.595-.033c-.573 0-1.058.096-1.454.287a2.52 2.52 0 0 0-.958.783 3.143 3.143 0 0 0-.518 1.158 6.32 6.32 0 0 0-.154 1.434v5.14h-3.128V10.407h2.973zM54.039 8.642V6.06h3.128v2.582H54.04zm3.128 1.765v11.405H54.04V10.407h3.128zM58.797 10.407h3.569l2.005 2.978 1.982-2.978h3.459l-3.745 5.339 4.208 6.067h-3.57l-2.378-3.596-2.38 3.596h-3.502l4.097-6.001zM74.094 31.25V.732H71.9V0h3.035v31.982H71.9v-.732z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,24 @@
export const DELEGATION_TYPES = {
LOCAL: "local",
WELL_KNOWN: ".well_known",
DNS: "DNS SRV",
};
export const REVERSE_PROXY_TYPES = {
CADDY: "CADDY",
APACHE: "APACHE",
HAPROXY: "HAPROXY",
NGINX: "NGINX",
OTHER: "OTHER",
};
export const TLS_TYPES = {
ACME: "ACME",
TLS: "TLS",
REVERSE_PROXY: "REVERSE_PROXY",
};
export const DATABASE_TYPES = {
SQLITE3: "sqlite3",
POSTGRES: "psycopg2",
};
@@ -0,0 +1,329 @@
import {
ADVANCE_UI,
BACK_UI,
SET_SERVERNAME,
SET_STATS,
BASE_CONFIG_CHECKED,
FAIL,
SET_SECRET_KEY,
GETTING_SECRET_KEY,
SET_DELEGATION,
SET_DELEGATION_SERVERNAME,
SET_DELEGATION_PORTS,
SET_REVERSE_PROXY,
SET_TLS,
TESTING_TLS_CERT_PATHS,
SET_TLS_CERT_PATHS,
SET_TLS_CERT_PATHS_VALIDITY,
SET_TLS_CERT_FILES,
UPLOADING_TLS_CERT_PATHS,
TESTING_SYNAPSE_PORTS,
SET_SYNAPSE_PORTS,
SET_SYNAPSE_PORTS_FREE,
SET_DATABASE,
SET_CONFIG_DIR,
SYNAPSE_START_FAILED,
} from './types';
import {
getServerSetup,
getSecretkey,
postCertPaths,
postCerts,
testPorts,
postConfig,
startSynapse,
} from '../api';
import { CONFIG_LOCK, CONFIG_DIR } from '../api/constants';
import { baseConfigToSynapseConfig } from '../utils/yaml';
export const startup = () => {
return dispatch => {
getServerSetup().then(
result => {
dispatch(start(result[CONFIG_LOCK]));
dispatch(setConfigDir(result[CONFIG_DIR]));
},
error => dispatch(fail(error)),
)
};
};
const setConfigDir = dir => ({
type: SET_CONFIG_DIR,
configDir: dir,
});
export const generateSecretKeys = serverName => {
return dispatch => {
dispatch(getSecretKey(serverName))
};
};
export const setTlsCertPaths = (certPath, certKeyPath, callback) => {
return dispatch => {
dispatch(testingTlsCertPaths(true));
postCertPaths(certPath, certKeyPath)
.then(
result => dispatch(checkTlsCertPathValidity(result, callback)),
error => dispatch(fail(error)),
);
};
};
const setTlsCerts = (certPath, certKeyPath) => ({
type: SET_TLS_CERT_PATHS,
certPath: certPath,
certKeyPath: certKeyPath,
});
const testingTlsCertPaths = testing => ({
type: TESTING_TLS_CERT_PATHS,
testing,
});
const checkTlsCertPathValidity =
({ cert_path: certPath, cert_key_path: certKeyPath }, callback) => {
return dispatch => {
dispatch(testingTlsCertPaths(false));
dispatch(setTlsCerts(certPath.absolute_path, certKeyPath.absolute_path))
dispatch(setCertPathValidity({ certPath, certKeyPath }));
if (!certPath.invalid && !certKeyPath.invalid) {
dispatch(advanceUI());
callback();
};
};
};
export const uploadTlsCertFiles = (tlsCertFile, tlsCertKeyFile) =>
dispatch => {
dispatch(setTlsCertFiles(tlsCertFile, tlsCertKeyFile));
dispatch(uploadingTlsCertFiles(true));
postCerts(tlsCertFile, tlsCertKeyFile)
.then(
result => {
dispatch(uploadingTlsCertFiles(false));
dispatch(advanceUI())
},
error => dispatch(fail(error)),
)
};
const uploadingTlsCertFiles = uploading => ({
type: UPLOADING_TLS_CERT_PATHS,
uploading,
});
export const setTlsCertFiles = (tlsCertFile, tlsCertKeyFile) => ({
type: SET_TLS_CERT_FILES,
tlsCertFile,
tlsCertKeyFile,
})
const setCertPathValidity = ({ certPath, certKeyPath }) => ({
type: SET_TLS_CERT_PATHS_VALIDITY,
certPathInvalid: certPath.invalid,
certKeyPathInvalid: certKeyPath.invalid,
});
export const gettingSecretKeys = () => ({
type: GETTING_SECRET_KEY,
});
export const getSecretKey = serverName => {
return dispatch => {
getSecretkey(serverName).then(
result => dispatch(setSecretKey(result)),
error => dispatch(fail(error)),
)
};
};
export const setSecretKey = key => ({
type: SET_SECRET_KEY,
key,
});
export const start = setupDone => ({
type: BASE_CONFIG_CHECKED,
setupDone,
});
export const fail = reason => ({
type: FAIL,
reason,
});
export const advanceUI = option => ({
type: ADVANCE_UI,
option,
});
export const setServername = servername => ({
type: SET_SERVERNAME,
servername,
});
export const setStats = consent => ({
type: SET_STATS,
consent,
});
export const setDelegation = delegationType => ({
type: SET_DELEGATION,
delegationType,
});
export const setDelegationServername = servername => ({
type: SET_DELEGATION_SERVERNAME,
servername,
});
export const setDelegationPorts = (federationPort, clientPort) => ({
type: SET_DELEGATION_PORTS,
federationPort,
clientPort,
});
export const setReverseProxy = proxyType => ({
type: SET_REVERSE_PROXY,
proxyType,
});
export const setTls = tlsType => ({
type: SET_TLS,
tlsType,
});
export const setSynapsePorts = (federationPort, clientPort, callback) => {
const fedPortPriv = federationPort < 1024;
const clientPortPriv = clientPort < 1024;
return dispatch => {
dispatch(testingSynapsePorts(true));
dispatch({
type: SET_SYNAPSE_PORTS,
federationPort,
clientPort,
})
testPorts([federationPort, clientPort])
.then(
results => dispatch(updatePortsFree(
fedPortPriv ? true : results.ports[0],
clientPortPriv ? true : results.ports[1],
callback,
)),
error => dispatch(fail(error)),
)
}
};
export const updatePortsFree =
(synapseFederationPortFree, synapseClientPortFree, callback) => {
return dispatch => {
dispatch(testingSynapsePorts(false));
dispatch({
type: SET_SYNAPSE_PORTS_FREE,
synapseFederationPortFree,
synapseClientPortFree,
});
if (synapseFederationPortFree && synapseClientPortFree) {
callback();
dispatch(advanceUI());
}
}
}
export const testingSynapsePorts = verifying => ({
type: TESTING_SYNAPSE_PORTS,
verifying,
})
export const setDatabase = databaseConfig => ({
type: SET_DATABASE,
databaseConfig,
})
export const writeConfig = (callback) => {
return (dispatch, getState) => {
postConfig(baseConfigToSynapseConfig(getState().baseConfig))
.then(
res => startSynapse().then(
res => {
if (res.ok) {
dispatch(advanceUI());
callback();
} else {
dispatch(synapseStartFailed());
}
},
error => {
fail(error);
dispatch(synapseStartFailed());
}
),
error => {
dispatch(fail(error));
dispatch(synapseStartStartFailed())
}
)
}
}
export const synapseStartFailed = () => ({
type: SYNAPSE_START_FAILED,
})
export const resetUI = (ui) => ({
type: BACK_UI,
ui,
})
@@ -0,0 +1,25 @@
export const ADVANCE_UI = 'ADVANCE_UI';
export const BACK_UI = 'BACK_UI';
export const SET_SERVERNAME = 'SET_SERVERNAME';
export const SET_STATS = 'SET_STATS';
export const BASE_CONFIG_CHECKED = 'BASE_CONFIG_CHECKED';
export const FAIL = 'NETWORK_FAIL';
export const SET_SECRET_KEY = 'SET_SECRET_KEY';
export const GETTING_SECRET_KEY = 'GETTING_SECRET_KEY';
export const SET_DELEGATION = 'SET_DELEGATION';
export const SET_DELEGATION_SERVERNAME = 'SET_DELEGATION_SERVERNAME';
export const SET_DELEGATION_PORTS = 'SET_DELEGATION_PORTS';
export const SET_REVERSE_PROXY = 'SET_REVERSE_PROXY';
export const TESTING_TLS_CERT_PATHS = 'TESTING_TLS_CERT_PATHS';
export const UPLOADING_TLS_CERT_PATHS = 'UPLOADING_TLS_CERT_PATHS';
export const SET_TLS = 'SET_TLS';
export const SET_TLS_CERT_PATHS = 'SET_TLS_CERT_PATHS';
export const SET_TLS_CERT_PATHS_VALIDITY = 'SET_TLS_CERT_PATHS_VALIDITY';
export const SET_TLS_CERT_FILES = 'SET_TLS_CERT_FILES';
export const TESTING_SYNAPSE_PORTS = 'TESTING_SYNAPSE_PORTS';
export const SET_SYNAPSE_PORTS = 'SET_SYNAPSE_PORTS';
export const SET_SYNAPSE_PORTS_FREE = 'SET_SYNAPSE_PORTS_IN_USE';
export const SET_DATABASE = 'SET_DATABASE';
export const SET_CONFIG_DIR = 'SET_CONFIG_DIR';
export const WRITE_CONFIG = 'WRITE_CONFIG';
export const SYNAPSE_START_FAILED = 'SYNAPSE_START_FAILED';
@@ -0,0 +1,11 @@
export const API_URL = "http://localhost:8888/";
export const SERVER_NAME = "/servername";
export const SECRET_KEY = "/secretkey";
export const CONFIG = "/config";
export const CONFIG_SOMETHING = "/config_something";
export const SETUP_CHECK = "/setup";
export const CERT_PATHS = "/testcertpaths";
export const TEST_PORTS = "/ports";
export const CONFIG_LOCK = "server_config_in_use";
export const CONFIG_DIR = "config_dir";
export const START = "/start";
@@ -0,0 +1,91 @@
import fetchAbsolute from 'fetch-absolute';
import {
API_URL,
CONFIG,
SECRET_KEY,
SERVER_NAME,
SETUP_CHECK,
CERT_PATHS,
TEST_PORTS,
START,
} from './constants';
const fetchAbs = fetchAbsolute(fetch)(API_URL)
export const getServerName = () =>
fetchAbs(SERVER_NAME)
.then(res => res.json())
export const postCertPaths = (certPath, certKeyPath) =>
fetchAbs(
CERT_PATHS,
{
method: 'POST',
body: JSON.stringify({
// eslint-disable-next-line camelcase
cert_path: certPath,
// eslint-disable-next-line camelcase
cert_key_path: certKeyPath,
}),
},
).then(res => res.json())
export const postCerts = (cert, certKey) =>
fetchAbs(
CERT_PATHS,
{
method: 'POST',
body: JSON.stringify({
cert,
// eslint-disable-next-line camelcase
cert_key: certKey,
}),
},
)
export const testPorts = (ports) =>
fetchAbs(
TEST_PORTS,
{
method: 'POST',
body: JSON.stringify({
ports,
}),
},
).then(res => res.json())
export const getSecretkey = serverName =>
fetchAbs(
SECRET_KEY,
{
method: 'POST',
body: JSON.stringify({
server_name: serverName,
})
}
)
.then(res => res.json())
.then(json => json.secret_key)
export const postConfig = (config) =>
fetchAbs(
CONFIG,
{
method: 'POST',
body: JSON.stringify(config),
},
);
// Checks if the server's base config has been setup.
export const getServerSetup = () => fetchAbs(SETUP_CHECK)
.then(res => res.json())
export const startSynapse = () => fetchAbs(
START,
{
method: 'POST',
}
)
@@ -0,0 +1,24 @@
import React from 'react';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { reset } from 'ansi-colors';
import Chevron from './Chevron';
export default ({ active, open, children, eventKey, as, reset }) => {
const clickable = active & !open;
const toggle = useAccordionToggle(eventKey);
const decoratedOnClick = () => {
if (clickable) {
toggle();
reset();
}
}
const As = as;
return <div className={clickable ? "active-card-header" : "inactive-card-header"}>
<As onClick={decoratedOnClick}> {children} <Chevron open={open} /></As>
</div>
}
@@ -0,0 +1,39 @@
/* eslint-disable max-len */
import React from 'react';
import ContentWrapper from '../containers/ContentWrapper';
import ButtonDisplay from './ButtonDisplay';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
export default ({ started, servername, onClick }) => {
const toggle = useAccordionToggle(nextUI())
const wrappedOnClick = () => {
onClick();
toggle();
}
const prompt = servername ? "Configuring " + servername : "Let's configure your Synapse server."
return <ContentWrapper>
<div className='baseintro'>
<svg width="113" height="48" xmlns="http://www.w3.org/2000/svg">
<g fill="#2D2D2D" fillRule="nonzero">
<path d="M1.404 1.098v45.776h3.29v1.099H.142V0h4.552v1.098zM14.079 15.61v2.317h.065c.617-.882 1.36-1.567 2.231-2.052.87-.485 1.867-.728 2.99-.728 1.08 0 2.066.21 2.958.629.892.42 1.57 1.158 2.032 2.217.507-.75 1.195-1.412 2.066-1.986.87-.573 1.9-.86 3.09-.86.903 0 1.74.11 2.511.33.77.222 1.432.575 1.982 1.06.55.485.98 1.12 1.29 1.902.307.784.462 1.727.462 2.83v11.45h-4.693v-9.696c0-.574-.022-1.114-.066-1.622-.044-.507-.165-.948-.363-1.323a2.21 2.21 0 0 0-.876-.894c-.385-.22-.909-.33-1.57-.33-.66 0-1.195.127-1.602.38a2.758 2.758 0 0 0-.958.993 3.981 3.981 0 0 0-.463 1.39 10.611 10.611 0 0 0-.116 1.572v9.53h-4.692v-9.597c0-.507-.011-1.009-.033-1.505a4.238 4.238 0 0 0-.28-1.374 2.117 2.117 0 0 0-.827-1.01c-.386-.252-.953-.38-1.702-.38-.22 0-.512.05-.875.15-.364.099-.717.287-1.058.562-.342.276-.633.673-.876 1.191-.242.519-.363 1.197-.363 2.035v9.928H9.65V15.61h4.428zM38.763 18.125a5.627 5.627 0 0 1 1.85-1.754 8.056 8.056 0 0 1 2.528-.943 14.368 14.368 0 0 1 2.825-.281c.86 0 1.73.06 2.61.182.881.121 1.686.359 2.413.711a4.757 4.757 0 0 1 1.784 1.473c.463.628.694 1.461.694 2.498v8.902c0 .773.044 1.511.132 2.217.088.706.242 1.236.463 1.588h-4.758a6.888 6.888 0 0 1-.33-1.655 6.117 6.117 0 0 1-2.644 1.623 10.625 10.625 0 0 1-3.107.463c-.815 0-1.575-.1-2.28-.298a5.207 5.207 0 0 1-1.85-.927 4.304 4.304 0 0 1-1.24-1.588c-.297-.64-.446-1.401-.446-2.283 0-.97.17-1.77.513-2.4a4.28 4.28 0 0 1 1.322-1.505 5.913 5.913 0 0 1 1.85-.844 19.932 19.932 0 0 1 2.098-.447c.705-.11 1.4-.198 2.082-.264a12.534 12.534 0 0 0 1.818-.298c.529-.133.947-.325 1.255-.58.308-.253.452-.622.43-1.108 0-.507-.083-.91-.248-1.208a1.825 1.825 0 0 0-.66-.695 2.606 2.606 0 0 0-.959-.331 7.938 7.938 0 0 0-1.173-.083c-.925 0-1.652.199-2.18.596-.53.397-.838 1.06-.926 1.986h-4.692c.065-1.103.34-2.02.826-2.747zm9.268 6.635c-.297.1-.617.182-.958.248-.341.067-.7.122-1.074.166-.374.044-.749.1-1.123.165-.353.066-.7.155-1.04.265-.343.11-.64.26-.893.447a2.125 2.125 0 0 0-.612.711c-.154.287-.231.651-.231 1.092 0 .419.077.773.231 1.059.154.287.364.513.628.678.265.166.573.282.926.348.352.066.715.099 1.09.099.925 0 1.64-.154 2.148-.463.506-.309.88-.679 1.123-1.11.242-.43.391-.864.447-1.306.054-.44.082-.794.082-1.059v-1.754a2.101 2.101 0 0 1-.744.414zM65.825 15.61v3.144H62.39v8.472c0 .793.132 1.323.397 1.588.263.264.793.397 1.586.397.264 0 .517-.011.76-.033a6.52 6.52 0 0 0 .693-.1v3.64c-.396.067-.837.11-1.321.133-.485.021-.959.033-1.421.033-.727 0-1.416-.05-2.065-.15a4.85 4.85 0 0 1-1.719-.578 3.06 3.06 0 0 1-1.173-1.224c-.286-.53-.43-1.225-.43-2.086V18.754h-2.841V15.61h2.842v-5.13h4.692v5.13h3.436zM72.533 15.61v3.177h.066c.22-.53.517-1.02.892-1.473a5.86 5.86 0 0 1 1.289-1.158 6.32 6.32 0 0 1 1.553-.745 5.601 5.601 0 0 1 1.719-.265c.308 0 .649.056 1.024.166v4.369a8.793 8.793 0 0 0-.793-.116 8.35 8.35 0 0 0-.893-.05c-.859 0-1.586.144-2.18.43a3.779 3.779 0 0 0-1.438 1.175 4.714 4.714 0 0 0-.776 1.737 9.48 9.48 0 0 0-.232 2.151v7.71h-4.692V15.61h4.461zM81.058 12.962V9.091h4.693v3.871h-4.693zm4.693 2.648v17.109h-4.693V15.61h4.693zM88.196 15.61h5.353l3.007 4.467 2.974-4.467h5.188L99.1 23.618l6.311 9.1h-5.353l-3.57-5.393-3.568 5.394h-5.254l6.146-9.001zM111.14 46.874V1.098h-3.289V0h4.552v47.973h-4.552v-1.099z" />
</g>
</svg>
<h1>Setting up Synapse</h1>
<p>{prompt}</p>
{
!started ?
<ButtonDisplay><button onClick={wrappedOnClick}>Get Started</button></ButtonDisplay>
: undefined
}
</div>
</ContentWrapper>
}
@@ -0,0 +1,5 @@
import React from 'react';
import style from '../../scss/main.scss';
export default ({ children }) => <div className={style.buttonDisplay}>{children}</div>
@@ -0,0 +1,7 @@
import React from 'react';
import { FaChevronRight, FaChevronDown } from 'react-icons/fa'
export default ({ open }) =>
<span className="chevron">
{open ? <FaChevronDown /> : <FaChevronRight />}
</span>
@@ -0,0 +1,94 @@
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import ReverseProxySampleConfig from '../containers/ReverseProxySampleConfig'
import DelegationSampleConfig from '../containers/DelegationSampleConfig';
import AccordionToggle from '../containers/AccordionToggle';
import InlineError from '../components/InlineError';
import { TLS_TYPES, DELEGATION_TYPES } from '../actions/constants';
import { COMPLETE_UI } from '../reducers/ui-constants';
import { nextUI } from '../reducers/setup-ui-reducer';
export default ({
tlsType,
delegationType,
synapseStartFailed,
configDir,
onClick,
}) => {
const toggle = useAccordionToggle(nextUI(COMPLETE_UI));
const decoratedOnClick = () => {
onClick(toggle);
}
const [revProxyDownloaded, setRevProxyDownloaded] = useState(false);
const [delegationDownloaded, setDelegationDownloaded] = useState(false);
const revProxyBody = <Card.Body>
<ReverseProxySampleConfig onClick={() => setRevProxyDownloaded(true)} />
<button
disabled={!revProxyDownloaded}
onClick={() => setBody(body + 1)}
>Next</button>
</Card.Body >
const delegationBody = <Card.Body>
<DelegationSampleConfig onClick={() => setDelegationDownloaded(true)} />
<button
disabled={!delegationDownloaded}
onClick={() => setBody(body + 1)}
>Next</button>
</Card.Body>
const finishedBody = <Card.Body>
<InlineError error={synapseStartFailed ? "Couldn't start synapse." : undefined}>
<button onClick={decoratedOnClick}>Start Synapse</button>
</InlineError>
<hr />
<p>
In future use <a href="https://manpages.debian.org/testing/matrix-synapse/synctl.1.en.html">
synctl</a> to start and stop synapse. Use the following to start synapse again:
</p>
<pre>
<code>
synctl start {configDir}
</code>
</pre>
</Card.Body>
const show = [];
const [body, setBody] = useState(0);
if (tlsType == TLS_TYPES.REVERSE_PROXY) {
show.push(revProxyBody);
}
if (delegationType != DELEGATION_TYPES.LOCAL) {
show.push(delegationBody)
}
show.push(finishedBody)
return <Card>
<AccordionToggle as={Card.Header} eventKey={COMPLETE_UI}>
Setup Complete
</AccordionToggle>
<Accordion.Collapse eventKey={COMPLETE_UI}>
{show[body]}
</Accordion.Collapse>
</Card>
}
@@ -0,0 +1,13 @@
import React, { useState } from 'react';
import ContentWrapper from '../containers/ContentWrapper';
export default () => {
return <ContentWrapper>
<h1>Config selection</h1>
<p>The base config has already been setup.</p>
<p>If you want to start the installation from scratch please delete the
config yaml.</p>
</ContentWrapper>;
}
@@ -0,0 +1,11 @@
import React from 'react';
import style from '../../scss/main.scss';
export default ({ children }) => {
return <div className={style.contentWrapper}>{children}</div>
}
@@ -0,0 +1,113 @@
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import {
DATABASE_TYPES,
} from '../actions/constants'
import { DATABASE_UI } from '../reducers/ui-constants';
import AccordionToggle from '../containers/AccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
import Tabs from 'react-bootstrap/Tabs';
import Tab from 'react-bootstrap/Tab';
export default ({
onClick,
}) => {
const defaultDatabase = DATABASE_TYPES.POSTGRES;
const [database, setDatabase] = useState(defaultDatabase)
const hostDefault = "localhost"
const [databaseHost, setHost] = useState(hostDefault);
const [postgresDatabase, setPostgresDatabase] = useState();
const [databaseUsername, setUser] = useState();
const [databasePassword, setPassword] = useState();
const toggle = useAccordionToggle(nextUI(DATABASE_UI));
return <Card>
<AccordionToggle as={Card.Header} eventKey={DATABASE_UI}>
Database
</AccordionToggle>
<Accordion.Collapse eventKey={DATABASE_UI}>
<Card.Body>
<p>Synapse can use either SQLite3 or Postgres as it's database.</p>
<p>Postgres is recommended.</p>
<Tabs defaultActiveKey={defaultDatabase} onSelect={k => setDatabase(k)}>
<Tab eventKey={DATABASE_TYPES.POSTGRES} title={"Postgres"}>
This will connect to the given Postgres database via {DATABASE_TYPES.POSTGRES}
<p>
Host
</p>
<input
type="text"
onChange={e => setHost(e.target.value ? e.target.value : hostDefault)}
autoFocus
placeholder="localhost"
/>
<p>
Database name
</p>
<input
type="text"
onChange={e => setPostgresDatabase(e.target.value)}
autoFocus
placeholder="unspecified"
/>
<p>
User name
</p>
<input
type="text"
onChange={e => setUser(e.target.value)}
autoFocus
placeholder="unspecified"
/>
<p>
Password
</p>
<input
type="text"
onChange={e => setPassword(e.target.value)}
autoFocus
placeholder="unspecified"
/>
<button
className='inputButton'
disabled={databaseHost ? undefined : true}
onClick={() => {
toggle();
onClick({
databaseType: DATABASE_TYPES.POSTGRES,
databaseHost,
database: postgresDatabase,
databaseUsername,
databasePassword,
})
}}
>Use Postgres</button>
</Tab>
<Tab eventKey={DATABASE_TYPES.SQLITE3} title={DATABASE_TYPES.SQLITE3}>
<button
className='inputButton'
onClick={() => {
toggle();
onClick({
databaseType: DATABASE_TYPES.SQLITE3
});
}}
>Use {DATABASE_TYPES.SQLITE3}</button>
</Tab>
</Tabs>
</Card.Body>
</Accordion.Collapse>
</Card >
}
@@ -0,0 +1,165 @@
/* eslint-disable max-len */
import React, { useState } from 'react';
import style from '../../scss/main.scss';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import Tabs from 'react-bootstrap/Tabs';
import Tab from 'react-bootstrap/Tab';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { DELEGATION_TYPES } from '../actions/constants';
import { DELEGATION_OPTIONS_UI } from '../reducers/ui-constants';
import AccordionToggle from '../containers/AccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
import InlineError from './InlineError';
export default ({ servername, skip, onClick }) => {
const defaultType = DELEGATION_TYPES.DNS;
const [type, setType] = useState(defaultType);
const [delegatedServername, setDelegatedServerName] = useState("");
const [fedPort, setFedPort] = useState("");
const [clientPort, setClientPort] = useState("");
const [clientPortValid, setClientPortValid] = useState(true)
const [fedPortValid, setFedPortValid] = useState(true)
const updateValidity = (port, setValid) => setValid(
!port ||
(!isNaN(port) && 0 < port && port <= 65535),
)
const onFederationChange = event => {
const val = event.target.value;
setFedPort(val);
updateValidity(val, setFedPortValid);
}
const onClientChange = event => {
const val = event.target.value;
setClientPort(val);
updateValidity(val, setClientPortValid);
}
const toggle = useAccordionToggle(nextUI(DELEGATION_OPTIONS_UI));
const portSelection = <div>
<p>Please enter the domain name of the server synapse is installed on.</p>
<input
type="text"
onChange={e => setDelegatedServerName(e.target.value)}
autoFocus
placeholder="Enter server name"
value={delegatedServername}
/>
<p>
Homeserver Port
</p>
<InlineError error={fedPortValid ? undefined : "Invalid port"}>
<input
type="text"
onChange={onFederationChange}
className={fedPortValid ? undefined : "invalid"}
autoFocus
placeholder="Use Default 8448"
value={fedPort}
/>
</InlineError>
<p>
Client Port
</p>
<InlineError error={clientPortValid ? undefined : "Invalid port"}>
<input
type="text"
onChange={onClientChange}
className={clientPortValid ? undefined : "invalid"}
autoFocus
placeholder="Use Default 443"
value={clientPort}
/>
</InlineError>
<button disabled={delegatedServername && clientPortValid && fedPortValid ? undefined : true}
onClick={() => {
toggle();
onClick(type, delegatedServername, fedPort, clientPort)
}}
>
Use {type}
</button>
</div >
return <Card>
<AccordionToggle as={Card.Header} eventKey={DELEGATION_OPTIONS_UI}>
Delegation (optional)
</AccordionToggle>
<Accordion.Collapse eventKey={DELEGATION_OPTIONS_UI}>
<Card.Body>
<p>
If you'd like your synapse to be hosted on a different server to the
one known on the network by '{servername}' you can use delegation.&nbsp;
<a
href="https://github.com/matrix-org/synapse/blob/master/docs/federate.md"
target="_blank"
>
Learn more
</a>
</p>
<p>
If you're not sure if you need delegation, we recommending skipping this step.
</p>
<button onClick={() => {
toggle();
skip();
}}>
Skip
</button>
<hr />
<p>
Other federation servers will connect to {servername}:8448 over the network.
</p>
<p>
There are two forms of delegation:
</p>
<Tabs defaultActiveKey={defaultType} onSelect={k => setType(k)}>
<Tab eventKey={DELEGATION_TYPES.DNS} title={DELEGATION_TYPES.DNS}>
<p>
You will need access to {servername}'s domain zone DNS records.
This method also requires the synapse install's server to provide
a valid TLS cert for {servername}
</p>
<p>
You will need to add an SRV record to {servername}'s DNS zone. (Once
again, we'll print the SRV record out for you later.)
</p>
{portSelection}
</Tab>
<Tab eventKey={DELEGATION_TYPES.WELL_KNOWN} title={DELEGATION_TYPES.WELL_KNOWN}>
<p>
{servername} provides the url
https://{servername}/.well-known/matrix/server which gives
federating servers information about how to contact the actual
server hosting the synapse install. (Don't worry! We'll print out
the .well-known file for you later.)
</p>
{portSelection}
</Tab>
</Tabs>
</Card.Body>
</Accordion.Collapse>
</Card>
}
@@ -0,0 +1,63 @@
/* eslint-disable max-len */
import React from 'react';
import ContentWrapper from '../containers/ContentWrapper';
import ButtonDisplay from './ButtonDisplay';
import DownloadOrCopy from './DownloadOrCopy';
import { DELEGATION_TYPES } from '../actions/constants';
export default ({
delegationType,
serverConfig,
clientConfig,
serverConfigFileName,
clientConfigFileName,
serverName,
onClick,
}) => {
if (delegationType == DELEGATION_TYPES.DNS) {
return <ContentWrapper>
<h1 className='setupCompleteTitle'>Configure Delegation</h1>
<p>
You will need to add the following SRV record to your DNS zone.
</p>
<pre>
<code>
{clientConfig}
</code>
</pre>
<DownloadOrCopy content={clientConfig} fileName={clientConfigFileName} onClick={onClick} />
</ContentWrapper>
} else {
return <ContentWrapper>
<h1 className='setupCompleteTitle'>Configure delegation</h1>
<p>
The delegation configuration needs to take place outside the installer.
</p>
<p>
You'll need to host the following at https://{serverName}/.well-known/matrix/server
</p>
<pre>
<code>
{serverConfig}
</code>
</pre>
<DownloadOrCopy content={serverConfig} fileName={serverConfigFileName} onClick={onClick} />
<p>
You'll also need to host the following at https://{serverName}/.well-known/matrix/client
</p>
<pre>
<code>
{clientConfig}
</code>
</pre>
<DownloadOrCopy content={clientConfig} fileName={clientConfigFileName} onClick={onClick} />
</ContentWrapper>;
}
}
@@ -0,0 +1,27 @@
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import AccordionToggle from '../containers/AccordionToggle';
import { DONE_UI } from '../reducers/ui-constants';
export default ({ configDir }) => {
return <Card>
<AccordionToggle as={Card.Header} eventKey={DONE_UI} >
Done
</AccordionToggle>
<Accordion.Collapse eventKey={DONE_UI}>
<Card.Body>
<p>
Synapse is running!
</p>
<p>
There are many settings to play with in the yaml files in <code>{configDir}</code>.
</p>
</Card.Body>
</Accordion.Collapse>
</Card>;
}
@@ -0,0 +1,43 @@
import React from 'react';
import ButtonDisplay from './ButtonDisplay';
const download = (filename, text) => {
const e = document.createElement('a');
e.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
e.setAttribute('download', filename);
e.style.display = 'none';
document.body.appendChild(e);
e.click();
document.body.removeChild(e);
}
export default ({ content, fileName, onClick = () => undefined }) => {
const downloadOnClick = () => {
download(fileName, content);
onClick();
}
const copyOnClick = () => {
navigator.clipboard.writeText(content);
onClick();
}
return <ButtonDisplay>
<div className='buttonGroup'>
<button onClick={downloadOnClick}>Download</button>
<span className='or'>or</span>
<button onClick={copyOnClick}>Copy</button>
</div>
</ButtonDisplay>
}
@@ -0,0 +1,12 @@
import React from 'react';
import ContentWrapper from '../containers/ContentWrapper';
export default () => {
return <ContentWrapper>
<h1>Damn!</h1>
<p>Has the config server been started?</p>
</ContentWrapper>;
}
@@ -0,0 +1,72 @@
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import ButtonDisplay from './ButtonDisplay';
import DownloadOrCopy from './DownloadOrCopy';
import { KEY_EXPORT_UI } from '../reducers/ui-constants';
import AccordionToggle from '../containers/AccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
export default ({ secretKeyLoaded, secretKey, onClick }) => {
const [downloadedOrCopied, setDownloadedOrCopied] = useState(false);
const toggle = useAccordionToggle(nextUI(KEY_EXPORT_UI));
const decoratedOnClick = () => {
setDownloadedOrCopied(false);
toggle();
onClick();
}
let body;
if (!secretKeyLoaded) {
body = <Card.Body><p>Generating secret key</p></Card.Body>
} else {
body = <Card.Body>
<p>
Your server uses a secret key to identify itself to other servers. Keep
a copy of it to retain ownership of the server name in case the server
is inaccessible:
</p>
<pre><code>{secretKey}</code></pre>
<p>
Keep a copy of this key somewhere safe by downloading or copying
the key to your clipboard to continue.
</p>
<DownloadOrCopy
content={secretKey}
fileName="secret_key.txt"
onClick={() => setDownloadedOrCopied(true)} />
<div className='blockWrapper'>
<ButtonDisplay>
<button
onClick={decoratedOnClick}
disabled={!downloadedOrCopied}
>Next</button>
</ButtonDisplay>
</div>
</Card.Body>
}
return <Card>
<AccordionToggle as={Card.Header} eventKey={KEY_EXPORT_UI}>
Secret Key
</AccordionToggle>
<Accordion.Collapse eventKey={KEY_EXPORT_UI}>
{body}
</Accordion.Collapse>
</Card>
}
@@ -0,0 +1,12 @@
import React from 'react';
export default ({ error, children }) => {
return <div
className="inlineError"
error={error ? "true" : "false"}>
{children}
{error ? <span>{error}</span> : undefined}
</div>
}
@@ -0,0 +1,11 @@
import React from 'react';
import ContentWrapper from '../containers/ContentWrapper';
export default () => {
return <ContentWrapper>
<h1>loading..</h1>
</ContentWrapper>;
}
@@ -0,0 +1,144 @@
/* eslint-disable max-len */
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { PORT_SELECTION_UI } from '../reducers/ui-constants';
import AccordionToggle from '../containers/AccordionToggle';
import ContentWrapper from '../containers/ContentWrapper';
import { nextUI } from '../reducers/setup-ui-reducer';
import InlineError from './InlineError';
export default ({
servername,
verifyingPorts,
fedPortInUse,
clientPortInUse,
canChangePorts,
defaultFedPort,
defaultClientPort,
onClick,
}) => {
if (verifyingPorts) {
return <ContentWrapper><h1>Verifying ports.</h1></ContentWrapper>
}
const [fedPort, setFedPort] = useState();
const [clientPort, setClientPort] = useState();
const [clientPortValid, setClientPortValid] = useState(true)
const [fedPortValid, setFedPortValid] = useState(true)
const updateValidity = (port, setValid) => setValid(
!isNaN(port) && 0 < port && port <= 65535,
)
const onFederationChange = event => {
const val = event.target.value ? event.target.value : defaultFedPort;
setFedPort(val);
updateValidity(val, setFedPortValid);
}
const onClientChange = event => {
const val = event.target.value ? event.target.value : defaultClientPort;
setClientPort(val);
updateValidity(val, setClientPortValid);
}
const toggle = useAccordionToggle(nextUI(PORT_SELECTION_UI));
const fedPortError = fedPortInUse ?
"This port is in use" :
!fedPortValid ? "Invalid port" :
undefined;
const clientPortError = clientPortInUse ?
"This port is in use" :
!clientPortValid ? "Invalid port" :
undefined;
const fedPortPriv = fedPort ? fedPort < 1024 : defaultFedPort < 1024
const clientPortPriv = clientPort ? clientPort < 1024 : defaultClientPort < 1024
return <Card>
<AccordionToggle as={Card.Header} eventKey={PORT_SELECTION_UI}>
{servername ? servername + "'s ports" : "Ports"}
</AccordionToggle>
<Accordion.Collapse eventKey={PORT_SELECTION_UI}>
<Card.Body>
<p>
Synapse will be listening on the following ports on localhost.
</p>
{
canChangePorts ?
<p>
Since you're using a reverse proxy you can change these to anything you
like as long as synapse can bind to them. We recommend not using privileged
ports within the range 0 to 1024.
</p>
:
<p>
Since you're not using a reverse proxy synapse will have to listen on
these ports. If any of these ports are already in use (we'll test them when
you click the button) you will either need to reconfigure the ports used on
localhost, setup up delegation or use a reverse proxy.
</p>
}
<p>
We will check that the ports are not in use.
</p>
<p>
Note: we can't check whether privileged ports are in use. If you've
set a privileged port <b>we will skip the check for that port</b>.
</p>
<h6>Federation Port</h6>
<InlineError error={fedPortError}>
<input
type="text"
onChange={onFederationChange}
disabled={canChangePorts ? undefined : true}
autoFocus
placeholder={defaultFedPort}
/>
</InlineError>
{fedPortPriv ? <p>This is a privileged port.</p> : undefined}
<h6>Client Port</h6>
<InlineError error={clientPortError}>
<input
type="text"
onChange={onClientChange}
disabled={canChangePorts ? undefined : true}
autoFocus
placeholder={defaultClientPort}
/>
</InlineError>
{clientPortPriv ? <p>This is a privileged port.</p> : undefined}
<div>
<button
disabled={clientPortValid && fedPortValid ? undefined : true}
onClick={() => onClick(
fedPort ? parseInt(fedPort) : defaultFedPort,
clientPort ? parseInt(clientPort) : defaultClientPort,
toggle,
)}
>Verify These Ports</button>
</div>
</Card.Body>
</Accordion.Collapse>
</Card>
}
@@ -0,0 +1,36 @@
import React from 'react';
import ContentWrapper from '../containers/ContentWrapper';
import ButtonDisplay from './ButtonDisplay';
import DownloadOrCopy from './DownloadOrCopy';
import { REVERSE_PROXY_TYPES } from '../actions/constants';
export default ({ proxyType, sampleConfig, fileName, onClick }) => {
return <ContentWrapper>
<h1 className='setupCompleteTitle'>Configure the ReverseProxy</h1>
<p>
It's time for you to setup the reverse proxy outside of this installer.
</p>
{
proxyType == REVERSE_PROXY_TYPES.OTHER ?
<p>
Here's a sample config for Apache. Since you chose 'other'
for your reverse proxy. You'll have to figure it out
for yourself. We believe in you.
</p>
:
<p>
We can't do it for you
but here's the sample configuration for your {proxyType} proxy.
</p>
}
<pre>
<code>
{sampleConfig}
</code>
</pre>
<DownloadOrCopy content={sampleConfig} fileName={fileName} onClick={onClick} />
</ContentWrapper>;
}
@@ -0,0 +1,67 @@
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import { SERVER_NAME_UI } from '../reducers/ui-constants';
import AccordionToggle from '../containers/AccordionToggle';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
import InlineError from './InlineError';
export default ({ onClick }) => {
const [servername, setServerName] = useState("");
const [serverNameValid, setServerNameValid] = useState(true);
const validator = /^[0-9a-zA-Z.-]+$/;
const onChange = event => {
setServerName(event.target.value);
setServerNameValid(validator.test(event.target.value));
};
const toggle = useAccordionToggle(nextUI(SERVER_NAME_UI));
const decoratedOnClick = () => {
onClick(servername);
toggle();
}
return <Card>
<AccordionToggle as={Card.Header} eventKey={SERVER_NAME_UI} >
Name your server
</AccordionToggle>
<Accordion.Collapse eventKey={SERVER_NAME_UI}>
<Card.Body>
<p>
Your server name usually matches your domain. For example, the
matrix.org server is simply called `matrix.org`.
</p>
<p>
Your server name will be used to establish User IDs (e.g.
`@user:server.name`) and Room Aliases (e.g. `#room:server.name`).
</p>
<InlineError error={!serverNameValid ? "The servername may only be alphanumeric characters" : undefined}>
<input
type="text"
onChange={onChange}
autoFocus
placeholder="Enter server name"
/>
</InlineError>
<div>
<button
disabled={servername && serverNameValid ? undefined : true}
onClick={decoratedOnClick}
>
Next
</button>
</div>
</Card.Body>
</Accordion.Collapse>
</Card>;
}
@@ -0,0 +1,48 @@
import React, { useState } from 'react';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { STATS_REPORT_UI } from '../reducers/ui-constants';
import AccordionToggle from '../containers/AccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
export default ({ onClick }) => {
const [consent, setConsent] = useState(false);
const toggle = useAccordionToggle(nextUI(STATS_REPORT_UI));
const decoratedOnClick = () => {
toggle();
onClick(consent);
}
return <Card>
<AccordionToggle as={Card.Header} eventKey={STATS_REPORT_UI}>
Anonymous Statistics
</AccordionToggle>
<Accordion.Collapse eventKey={STATS_REPORT_UI}>
<Card.Body>
<p>
Would you like to report anonymous statistics to matrix.org?
Your server will send anonymised, aggregated statistics to matrix.org
on user usage so we can measure the health of the Matrix ecosystem.
</p>
<label>
<input
type="checkbox"
onChange={event => setConsent(event.target.checked)}
/>
Yes, send anonymous statistics
</label>
<div className='blockWrapper'>
<button onClick={decoratedOnClick}>Next</button>
</div>
</Card.Body>
</Accordion.Collapse>
</Card>
}
@@ -0,0 +1,164 @@
/* eslint-disable max-len */
import React, { useState } from 'react';
import style from '../../scss/main.scss';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import Tabs from 'react-bootstrap/Tabs';
import Tab from 'react-bootstrap/Tab';
import useAccordionToggle from 'react-bootstrap/useAccordionToggle';
import { TLS_UI } from '../reducers/ui-constants';
import { TLS_TYPES, REVERSE_PROXY_TYPES } from '../actions/constants';
import AccordionToggle from '../containers/AccordionToggle';
import { nextUI } from '../reducers/setup-ui-reducer';
import InlineError from './InlineError';
const tlsLink = "https://en.wikipedia.org/wiki/Transport_Layer_Security";
const apacheLink = "http://httpd.apache.org/";
const caddyLink = "https://caddyserver.com/";
const haproxyLink = "http://www.haproxy.org/";
const nginxLink = "https://www.nginx.com/";
export default ({
testingCertPaths,
certPathInvalid,
certKeyPathInvalid,
onClickCertPath,
onClickACME,
onClickReverseProxy,
}) => {
const defaultType = TLS_TYPES.REVERSE_PROXY;
const [certPath, setCertPath] = useState("");
const [certKeyPath, setCertKeyPath] = useState("");
const defaultValue = REVERSE_PROXY_TYPES.NGINX;
const [reverseProxy, setReverseProxy] = useState(defaultValue);
const toggle = useAccordionToggle(nextUI(TLS_UI));
return <Card>
<AccordionToggle as={Card.Header} eventKey={TLS_UI}>
TLS
</AccordionToggle>
<Accordion.Collapse eventKey={TLS_UI}>
<Card.Body>
<p>
Synapse uses TLS to ensure communication between homeservers is
secure. To use TLS, youll need a TLS certificate. Synapse supports
ACME, providing your own certificates, or reverse proxy handling TLS
certificates.
</p>
<Tabs defaultActiveKey={defaultType}>
<Tab eventKey={TLS_TYPES.REVERSE_PROXY} title="Reverse Proxy">
<p>
It is recommended to run Synapse behind a reverse proxy such
as <a target="_blank" href={apacheLink}>Apache</a>, <a target="_blank" href={caddyLink}>Caddy</a>, <a target="_blank" href={haproxyLink}>HAProxy</a>, or <a target="_blank" href={nginxLink}>NGiNX</a>.
</p>
<p>
The main benefit to this is that the reverse proxy can listen on
the privileged port 443 (which clients like Riot expect to connect
to) on behalf of synapse. The incoming traffic is then forwarded
to Synapse on a non privileged port.
</p>
<p>
You need root to listen on ports 0 to 1024 inclusive and running
synapse with root privileges is <b>strongly discouraged</b>.
Reverse proxies are more secure, run with root and pass things on
like nobody's business.
</p>
<p>
(Note: you can also have synapse use a non privileged port by
using one of the delegation methods mentioned earlier.)
</p>
<p>
If you choose to use a Reverse Proxy we'll provide you with
configuration templates later.
</p>
<p>More information about Reverse Proxies{' '}
<a href="https://github.com/matrix-org/synapse/blob/master/docs/reverse_proxy.rst">
in the docs</a>.
</p>
<p>
Please choose the reverse proxy you're using. This is just so we can provide
you with a template later, if you already know how you're going to set yours
up don't worry too much about this.
</p>
<select defaultValue={defaultValue} onChange={e => setReverseProxy(e.target.value)} >
<option value={REVERSE_PROXY_TYPES.APACHE}>Apache</option>
<option value={REVERSE_PROXY_TYPES.CADDY}>Caddy</option>
<option value={REVERSE_PROXY_TYPES.HAPROXY}>HAProxy</option>
<option value={REVERSE_PROXY_TYPES.NGINX}>NGiNX</option>
<option value={REVERSE_PROXY_TYPES.OTHER}>Some other Reverse Proxy</option>
</select>
<div>
<button onClick={() => {
toggle();
onClickReverseProxy(reverseProxy)
}}>
Use a reverse proxy with TLS
</button>
</div>
</Tab>
<Tab eventKey={TLS_TYPES.ACME} title="ACME">
<p>
ACME is a protocol that allows TLS certificates to be requested
automagically. Synapse supports ACME by requesting certs from
Let's Encrypt, which is one of the easiest ways to manage your
certificates.
</p>
<p>
If you wish to use ACME you will need access to port 80 which
usually requires root privileges. Do not run Synapse as root. Use
a Reverse Proxy or Authbind
</p>
<button onClick={() => {
toggle();
onClickACME()
}}>Use ACME</button>
</Tab>
<Tab eventKey={TLS_TYPES.TLS} title="Provide your own TLS certs">
<p>
Specify a path to or upload TLS certs for the domain.
</p>
<InlineError error={certPathInvalid ? "The file doesn't exist or can't be accessed." : undefined}>
<input
className={certPathInvalid ? style.invalidInput : undefined}
type="text"
placeholder="/path/to/your/cert.pem"
value={certPath ? certPath : undefined}
onChange={e => setCertPath(e.target.value)}
/>
</InlineError>
<p>Please enter path to the cert's key</p>
<InlineError error={certKeyPathInvalid ? "The file doesn't exist or can't be accessed." : undefined}>
<input
className={certKeyPathInvalid ? style.invalidInput : undefined}
type="text"
placeholder="/path/to/your/cert/key.tls.key"
value={certKeyPath ? certKeyPath : undefined}
onChange={e => setCertKeyPath(e.target.value)}
/>
</InlineError>
<button
className="inputButton"
disabled={certPath && certKeyPath ? undefined : true}
onClick={() => onClickCertPath(certPath, certKeyPath, toggle)}
>Use TLS Path</button>
</Tab>
</Tabs>
</Card.Body>
</Accordion.Collapse>
</Card>
}
@@ -0,0 +1,108 @@
import React from 'react';
import style from '../../scss/main.scss';
import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import {
SERVER_NAME_UI,
STATS_REPORT_UI,
KEY_EXPORT_UI,
DELEGATION_OPTIONS_UI,
TLS_UI,
PORT_SELECTION_UI,
REVERSE_PROXY_TEMPLATE_UI,
LOADING_UI,
ERROR_UI,
DELEGATION_TEMPLATE_UI,
DATABASE_UI,
COMPLETE_UI,
SETUP_ORDER,
DONE_UI,
} from '../reducers/ui-constants';
import Error from './Error';
import Loading from './Loading';
import BaseIntro from '../containers/BaseIntro';
import ServerName from '../containers/ServerName';
import StatsReporter from '../containers/StatsReporter';
import ExportKeys from '../containers/ExportKeys';
import DelegationOptions from '../containers/DelegationOptions';
import TLS from '../containers/TLS';
import PortSelection from '../containers/PortSelection';
import ReverseProxySampleConfig from '../containers/ReverseProxySampleConfig';
import DelegationSampleConfig from '../containers/DelegationSampleConfig';
import Database from '../containers/Database';
import ConfigCompleted from './ConfigCompleted';
import CompleteSetup from '../containers/CompleteSetup';
import ContentWrapper from '../containers/ContentWrapper';
import Done from '../containers/Done';
const blockMapping = uiBlock => {
switch (uiBlock) {
case LOADING_UI:
return <Loading key={uiBlock} />
case ERROR_UI:
return <Error key={uiBlock} />
case SERVER_NAME_UI:
return <ServerName key={uiBlock} />
case STATS_REPORT_UI:
return <StatsReporter key={uiBlock} />
case KEY_EXPORT_UI:
return <ExportKeys key={uiBlock} />
case DELEGATION_OPTIONS_UI:
return <DelegationOptions key={uiBlock} />
case TLS_UI:
return <TLS key={uiBlock} />
case PORT_SELECTION_UI:
return <PortSelection key={uiBlock} />
case REVERSE_PROXY_TEMPLATE_UI:
return <ReverseProxySampleConfig key={uiBlock} />
case DELEGATION_TEMPLATE_UI:
return <DelegationSampleConfig key={uiBlock} />
case DATABASE_UI:
return <Database key={uiBlock} />
case COMPLETE_UI:
return <CompleteSetup key={uiBlock} />
case DONE_UI:
return <Done key={uiBlock} />
default:
return <h1>how did i get here?</h1>
}
}
export default ({ setupUI, configUI, baseConfig }) => {
if (!baseConfig.baseConfigChecked) {
return <Loading />
}
if (baseConfig.setupDone) {
console.log(`switching to ui ${configUI}`);
return <ConfigCompleted></ConfigCompleted>
}
if (!baseConfig.setupDone) {
return <ContentWrapper>
<Accordion>
<BaseIntro />
<div style={setupUI.activeBlocks.length ? undefined : { display: "none" }}>
{SETUP_ORDER.map(blockMapping)}
</div>
</Accordion>
</ContentWrapper>
}
}
@@ -0,0 +1,22 @@
import { connect } from 'react-redux';
import AccordionToggle from '../components/AccordionToggle';
import { resetUI } from '../actions';
import { DONE_UI } from '../reducers/ui-constants';
const mapStateToProps = (state, { eventKey, as, children }) => ({
active: state.setupUI.activeBlocks.includes(eventKey) && state.setupUI.activeBlocks[state.setupUI.activeBlocks.length - 1] != DONE_UI,
open: state.setupUI.activeBlocks[state.setupUI.activeBlocks.length - 1] == eventKey,
eventKey,
as,
children,
});
const mapDispathToProps = (dispatch, { eventKey }) => ({
reset: () => dispatch(resetUI(eventKey)),
});
export default connect(
mapStateToProps,
mapDispathToProps,
)(AccordionToggle);
@@ -0,0 +1,19 @@
import { connect } from 'react-redux';
import BaseIntro from '../components/BaseIntro';
import { advanceUI } from '../actions';
const mapStateToProps = (state, ownProps) => ({
started: Boolean(state.setupUI.activeBlocks.length),
servername: state.baseConfig.servername,
});
const mapDispathToProps = (dispatch) => ({
onClick: () => dispatch(advanceUI()),
});
export default connect(
mapStateToProps,
mapDispathToProps,
)(BaseIntro);
@@ -0,0 +1,21 @@
import { connect } from 'react-redux';
import CompleteSetup from '../components/CompleteSetup';
import { writeConfig } from '../actions';
const mapStateToProps = (state) => ({
tlsType: state.baseConfig.tls,
synapseStartFailed: state.baseConfig.synapseStartFailed,
delegationType: state.baseConfig.delegationType,
configDir: state.baseConfig.configDir,
});
const mapDispatchToProps = (dispatch) => ({
onClick: (callback) => dispatch(writeConfig(callback)),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(CompleteSetup);
@@ -0,0 +1,15 @@
import { connect } from 'react-redux';
import ContentWrapper from '../components/ContentWrapper';
const mapStateToProps = (state, { children }) => ({
children,
});
const mapDispatchToProps = (dispatch) => ({
});
export default connect(
mapStateToProps,
)(ContentWrapper);
@@ -0,0 +1,22 @@
import { connect } from 'react-redux';
import Database from '../components/Database';
import { setDatabase, advanceUI } from '../actions';
const mapStateToProps = (state) => {
};
const mapDispatchToProps = (dispatch) => ({
onClick: databaseConfig => {
dispatch(setDatabase(databaseConfig));
dispatch(advanceUI());
},
});
export default connect(
null,
mapDispatchToProps,
)(Database);
@@ -0,0 +1,44 @@
import { connect } from 'react-redux';
import DelegationOptions from '../components/DelegationOptions';
import {
setDelegation,
advanceUI,
setDelegationServername,
setDelegationPorts,
} from '../actions';
import { DELEGATION_TYPES } from '../actions/constants';
const mapStateToProps = (state, { children }) => {
return {
servername: state.baseConfig.servername,
}
}
const mapDispatchToProps = (dispatch) => ({
onClick: (type, servername, fedPort, clientPort) => {
dispatch(advanceUI());
dispatch(setDelegation(type));
dispatch(setDelegationServername(servername));
dispatch(setDelegationPorts(fedPort, clientPort));
},
skip: () => {
dispatch(advanceUI());
dispatch(setDelegation(DELEGATION_TYPES.LOCAL));
},
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(DelegationOptions);
@@ -0,0 +1,74 @@
import { connect } from 'react-redux';
import DelegationSampleConfig from '../components/DelegationSampleConfig';
import { advanceUI } from '../actions';
import DNSConfig from '../templates/dns-srv';
import FedWellKnownConfig from '../templates/federation-well-known'
import ClientWellKnownConfig from '../templates/client-well-known'
import { DELEGATION_TYPES } from '../actions/constants';
// synapseServerName: state.baseConfig.delegationServerName ? state.baseConfig.delegationServerName : state.baseConfig.servername,
const serverConfig = state => {
if (state.delegationType == DELEGATION_TYPES.DNS) {
return undefined;
} else {
return FedWellKnownConfig({
synapseServerName: state.delegationServerName,
delegationSynapsePort: state.delegationFederationPort ?
state.delegationFederationPort :
8448,
});
}
}
const clientConfig = state => {
if (state.delegationType == DELEGATION_TYPES.WELL_KNOWN) {
return ClientWellKnownConfig({
synapseServerName: state.delegationServerName,
delegationClientPort: state.delegationClientPort ?
state.delegationClientPort :
443,
});
} else {
return DNSConfig({
serverName: state.servername,
synapseServerName: state.delegationServerName,
delegationClientPort: state.delegationClientPort ?
state.delegationClientPort :
443,
})
}
}
const mapStateToProps = state => ({
delegationType: state.baseConfig.delegationType,
serverConfig: serverConfig(state.baseConfig),
clientConfig: clientConfig(state.baseConfig),
serverConfigFileName: `${state.baseConfig.servername}_delegation.conf`,
clientConfigFileName: `${state.baseConfig.servername}_client_delegation.conf`,
serverName: state.baseConfig.servername,
});
const mapDispatchToProps = (dispatch, { onClick }) => ({
onClick,
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(DelegationSampleConfig);
@@ -0,0 +1,16 @@
import { connect } from 'react-redux';
import Done from '../components/Done';
const mapStateToProps = (state, ownProps) => ({
configDir: state.baseConfig.configDir,
});
const mapDispathToProps = (dispatch) => ({
});
export default connect(
mapStateToProps,
mapDispathToProps,
)(Done);
@@ -0,0 +1,25 @@
import { connect } from 'react-redux';
import ExportKeys from '../components/ExportKeys';
import { advanceUI } from '../actions';
const mapStateToProps = state => {
const secretKeyLoaded = state.baseConfig.secretKeyLoaded;
const secretKey = state.baseConfig.secretKey;
return {
secretKeyLoaded,
secretKey,
}
};
const mapDispatchToProps = dispatch => ({
onClick: () => dispatch(advanceUI()),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ExportKeys);
@@ -0,0 +1,59 @@
import { connect } from 'react-redux';
import PortSelection from '../components/PortSelection';
import { setSynapsePorts } from '../actions';
import { TLS_TYPES } from '../actions/constants';
const defaultFedPort = state => {
if (state.tls == TLS_TYPES.REVERSE_PROXY) {
return 8008;
}
return state.delegationFederationPort ? state.delegationFederationPort : 8448;
}
const defaultClientPort = state => {
if (state.tls == TLS_TYPES.REVERSE_PROXY) {
return 8008;
}
return state.delegationFederationPort ?
state.delegationFederationPort :
443;
}
const mapStateToProps = ({ baseConfig }) => ({
servername: baseConfig.servername,
verifyingPorts: baseConfig.verifyingPorts,
fedPortInUse: baseConfig.synapseFederationPortFree != undefined ?
!baseConfig.synapseFederationPortFree :
false,
clientPortInUse: baseConfig.synapseClientPortFree != undefined ?
!baseConfig.synapseClientPortFree :
false,
canChangePorts: baseConfig.tls == TLS_TYPES.REVERSE_PROXY,
defaultFedPort: defaultFedPort(baseConfig),
defaultClientPort: defaultClientPort(baseConfig),
});
const mapDispathToProps = (dispatch) => ({
onClick: (fedPort, clientPort, callback) => {
dispatch(setSynapsePorts(fedPort, clientPort, callback));
},
});
export default connect(
mapStateToProps,
mapDispathToProps,
)(PortSelection);
@@ -0,0 +1,58 @@
import { connect } from 'react-redux';
import ReverseProxySampleConfig from '../components/ReverseProxySampleConfig';
import { REVERSE_PROXY_TYPES } from '../actions/constants';
import apacheConfig from '../templates/apache';
import caddyConfig from '../templates/caddy';
import haproxyConfig from '../templates/haproxy';
import nginxConfig from '../templates/nginx';
const sampleConfig = reverseProxyType => {
switch (reverseProxyType) {
case REVERSE_PROXY_TYPES.APACHE:
return apacheConfig;
case REVERSE_PROXY_TYPES.CADDY:
return caddyConfig;
case REVERSE_PROXY_TYPES.HAPROXY:
return haproxyConfig;
case REVERSE_PROXY_TYPES.NGINX:
return nginxConfig;
case REVERSE_PROXY_TYPES.OTHER:
return otherConfig;
default:
return () => { }
}
}
const mapStateToProps = state => ({
proxyType: state.baseConfig.reverseProxy,
sampleConfig: sampleConfig(state.baseConfig.reverseProxy)({
delegationFedPort: state.baseConfig.delegationFederationPort ?
state.baseConfig.delegationFederationPort :
8448,
delegationClientPort: state.baseConfig.delegationClientPort ?
state.baseConfig.delegationClientPort :
443,
fedPort: state.baseConfig.synapseFederationPort,
clientPort: state.baseConfig.synapseClientPort,
synapseServerName: state.baseConfig.delegationServerName ?
state.baseConfig.delegationServerName :
state.baseConfig.servername,
}),
fileName: "synapse_reverse_proxy.conf",
});
const mapDispatchToProps = (dispatch, { onClick }) => ({
onClick,
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ReverseProxySampleConfig);
@@ -0,0 +1,24 @@
import { connect } from 'react-redux';
import ServerName from '../components/ServerName';
import { advanceUI, setServername, generateSecretKeys } from '../actions';
const mapStateToProps = (state, ownProps) => ({
});
const mapDispathToProps = (dispatch) => ({
onClick: servername => {
dispatch(advanceUI());
dispatch(setServername(servername));
dispatch(generateSecretKeys(servername));
},
});
export default connect(
null,
mapDispathToProps,
)(ServerName);
@@ -0,0 +1,23 @@
import { connect } from 'react-redux';
import StatsReporter from '../components/StatsReporter';
import { advanceUI, setStats } from '../actions';
const mapStateToProps = (state, ownProps) => ({
});
const mapDispathToProps = (dispatch) => ({
onClick: consent => {
dispatch(advanceUI());
dispatch(setStats(consent));
},
});
export default connect(
null,
mapDispathToProps,
)(StatsReporter);
@@ -0,0 +1,52 @@
import { connect } from 'react-redux';
import TLS from '../components/TLS';
import {
advanceUI,
setTls,
setTlsCertPaths,
setTlsCertFiles,
setReverseProxy,
} from '../actions';
import { TLS_TYPES } from '../actions/constants';
const mapStateToProps = (state, ownProps) => ({
testingCertPaths: state.baseConfig.testingCertPaths,
uploadingCertPaths: state.baseConfig.uploadingCerts,
certPathInvalid: state.baseConfig.certPathInvalid,
certKeyPathInvalid: state.baseConfig.certKeyPathInvalid,
});
const mapDispathToProps = (dispatch) => ({
onClickACME: () => {
dispatch(advanceUI(TLS_TYPES.ACME));
dispatch(setTls(TLS_TYPES.ACME));
},
onClickReverseProxy: proxyType => {
dispatch(advanceUI());
dispatch(setTls(TLS_TYPES.REVERSE_PROXY))
dispatch(setReverseProxy(proxyType))
},
onClickCertPath: (certPath, certKeyPath, callback) => {
dispatch(setTlsCertPaths(certPath, certKeyPath, callback));
},
onClickCertUpload: (tlsCertFile, tlsKeyFile, callback) => {
dispatch(setTlsCertFiles(tlsCertFile, tlsKeyFile));
callback();
},
});
export default connect(
mapStateToProps,
mapDispathToProps,
)(TLS)
@@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import UI from '../components/UI';
const mapStateToProps = ({ setupDone, setupUI, configUI, baseConfig }) => ({
setupDone,
setupUI,
configUI,
baseConfig,
})
const mapDispathToProps = (dispatch, ownProps) => ({
})
export default connect(
mapStateToProps,
)(UI)
+25
View File
@@ -0,0 +1,25 @@
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import UI from './containers/UI';
import { startup } from './actions';
const store = createStore(
rootReducer,
applyMiddleware(thunk),
//+ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
store.dispatch(startup());
render(
<Provider store={store}>
{/* <img className={style.logo} src={logo} /> */}
<UI />
</Provider>,
document.body,
);
@@ -0,0 +1,147 @@
import {
SET_SERVERNAME,
SET_STATS,
SET_SECRET_KEY,
GETTING_SECRET_KEY,
SET_DELEGATION,
SET_DELEGATION_PORTS,
SET_DELEGATION_SERVERNAME,
SET_REVERSE_PROXY,
SET_TLS,
TESTING_TLS_CERT_PATHS,
SET_TLS_CERT_PATHS,
SET_TLS_CERT_PATHS_VALIDITY,
SET_TLS_CERT_FILES,
UPLOADING_TLS_CERT_PATHS,
TESTING_SYNAPSE_PORTS,
SET_SYNAPSE_PORTS,
SET_SYNAPSE_PORTS_FREE,
SET_DATABASE,
SET_CONFIG_DIR,
BASE_CONFIG_CHECKED,
SYNAPSE_START_FAILED,
} from "../actions/types";
export default (state, action) => {
switch (action.type) {
case BASE_CONFIG_CHECKED:
return {
...state,
baseConfigChecked: true,
setupDone: action.setupDone,
}
case SET_SERVERNAME:
return {
...state,
servername: action.servername,
}
case SET_STATS:
return {
...state,
reportStats: action.consent,
}
case GETTING_SECRET_KEY:
return {
...state,
secretKeyLoaded: false,
}
case SET_SECRET_KEY:
return {
...state,
secretKeyLoaded: true,
secretKey: action.key,
};
case SET_DELEGATION:
return {
...state,
delegationType: action.delegationType,
}
case SET_DELEGATION_PORTS:
return {
...state,
delegationFederationPort: action.federationPort,
delegationClientPort: action.clientPort,
}
case SET_DELEGATION_SERVERNAME:
return {
...state,
delegationServername: action.servername,
}
case SET_REVERSE_PROXY:
return {
...state,
reverseProxy: action.proxyType,
}
case SET_TLS:
return {
...state,
tls: action.tlsType,
}
case TESTING_TLS_CERT_PATHS:
return {
...state,
testingCertPaths: action.testing,
}
case SET_TLS_CERT_PATHS_VALIDITY:
return {
...state,
certPathInvalid: action.certPathInvalid,
certKeyPathInvalid: action.certKeyPathInvalid,
}
case SET_TLS_CERT_PATHS:
return {
...state,
tlsCertPath: action.certPath,
tlsCertKeyPath: action.certKeyPath,
}
case SET_TLS_CERT_FILES:
return {
...state,
tlsCertFile: action.tlsCertFile,
tlsCertKeyFile: action.tlsCerKeyFile,
}
case UPLOADING_TLS_CERT_PATHS:
return {
...state,
uploadingCerts: action.uploading,
}
case TESTING_SYNAPSE_PORTS:
return {
...state,
verifyingports: action.verifying,
}
case SET_SYNAPSE_PORTS:
return {
...state,
synapseFederationPort: action.federationPort,
synapseClientPort: action.clientPort,
}
case SET_SYNAPSE_PORTS_FREE:
return {
...state,
synapseFederationPortFree: action.synapseFederationPortFree,
synapseClientPortFree: action.synapseClientPortFree,
}
case SET_DATABASE:
return {
...state,
...action.databaseConfig,
}
case SET_CONFIG_DIR:
return {
...state,
configDir: action.configDir,
}
case SYNAPSE_START_FAILED:
return {
...state,
synapseStartFailed: true,
}
default:
return state;
}
};
@@ -0,0 +1,21 @@
const ADVANCED_CONFIG_UI_COMPONENTS = {
CONFIG_SELECTION_UI: "config_selection_ui",
};
export default ({ configUI, baseConfig }, action) => {
if (!baseConfig.baseConfigChecked) {
return configUI;
};
if (!baseConfig.setupDone) {
return configUI;
};
return configUI;
}
@@ -0,0 +1,75 @@
import baseConfigReducer from './base-config-reducer';
import configUIReducer from './config-ui-reducer';
import setupUIReducer from './setup-ui-reducer';
import { uiStateMapping } from './state';
import { BACK_UI } from '../actions/types';
export default (state = {
setupUI: {
activeBlocks: [],
},
configUI: {
},
baseConfig: {
baseConfigChecked: false,
},
}, action) => {
const setupUI = setupUIReducer(state, action);
const rState = {
configUI: configUIReducer(state, action),
setupUI,
baseConfig: filterBaseConfig(
baseConfigReducer(state.baseConfig, action),
action,
setupUI.activeBlocks.slice(0, setupUI.activeBlocks.length - 1),
),
}
console.log(action);
console.log(rState);
return rState;
};
const filterBaseConfig = (baseConfig, action, activeBlocks) => {
if (action.type == BACK_UI) {
return filterObj(
baseConfig,
Object.values(
filterObj(
uiStateMapping,
[...activeBlocks, "base"]),
).flat(),
);
} else {
return baseConfig;
}
}
const filterObj = (object, filterList) => {
return Object.keys(object)
.filter(key => filterList.includes(key))
.reduce((obj, key) => {
obj[key] = object[key];
return obj;
},
{},
);
}
@@ -0,0 +1,57 @@
import { ADVANCE_UI, BACK_UI, BASE_CONFIG_CHECKED } from '../actions/types';
import {
SETUP_ORDER,
} from './ui-constants';
const newActiveBlocks = activeBlocks => {
return SETUP_ORDER.slice(0, activeBlocks.length + 1)
}
export default ({ setupUI, baseConfig }, action) => {
if (!baseConfig.baseConfigChecked) {
return setupUI;
}
if (baseConfig.setupDone) {
return setupUI;
}
switch (action.type) {
case ADVANCE_UI:
return {
activeBlocks: newActiveBlocks(setupUI.activeBlocks),
};
case BACK_UI:
return {
activeBlocks: resetUI(setupUI.activeBlocks, action.ui),
};
default:
return setupUI;
}
}
export const nextUI = current => SETUP_ORDER[SETUP_ORDER.lastIndexOf(current) + 1]
export const resetUI = (activeBlocks, destinationBlock) => {
const indexOfDest = SETUP_ORDER.indexOf(destinationBlock);
if (indexOfDest >= activeBlocks.length) {
// The index is in the future
return activeBlocks;
}
return activeBlocks.slice(0, indexOfDest + 1);
}
@@ -0,0 +1,129 @@
import {
SERVER_NAME_UI,
STATS_REPORT_UI,
KEY_EXPORT_UI,
DELEGATION_OPTIONS_UI,
PORT_SELECTION_UI,
DATABASE_UI,
COMPLETE_UI,
TLS_UI,
} from './ui-constants';
const setupUI = "setupUI";
const activeBlocks = "activeBlocks";
const configUI = "configUI";
const baseConfig = "baseConfig";
const setupDone = "setupDone";
const baseConfigChecked = "baseConfigChecked";
const servername = "servername";
const reportStats = "reportStats";
const secretKeyLoaded = "secretKeyLoaded";
const secretKey = "secretKey";
const delegationType = "delegationType";
const delegationServerName = "delegationServerName";
const delegationFederationPort = "delegationFederationPort";
const delegationClientPort = "delegationClientPort";
const reverseProxy = "reverseProxy";
const tls = "tls";
const testingCertPaths = "testingCertPaths";
const uploadingCerts = "uploadingCerts";
const certPathInvalid = "certPathInvalid";
const certKeyPathInvalid = "certKeyPathInvalid";
const tlsCertPath = "tlsCertPath";
const tlsCertKeyPath = "tlsCertKeyPath";
const tlsCertFile = "tlsCertFile";
const tlsCertKeyFile = "tlsCertKeyFile";
const tlsPath = "tlsPath";
const verifyingPorts = "verifyingPorts";
const synapseFederationPortFree = "synapseFederationPortFree";
const synapseClientPortFree = "synapseClientPortFree";
const synapseFederationPort = "synapseFederationPort";
const synapseClientPort = "synapseClientPort";
const databaseConf = "databaseConf";
const configDir = "configDir";
const state = {
[setupUI]: {
[activeBlocks]: ["block1"],
},
[configUI]: {
},
[baseConfig]: {
[setupDone]: true,
[baseConfigChecked]: false,
[configDir]: "sadfasdf",
[servername]: "server_name",
[reportStats]: false,
[secretKeyLoaded]: false,
[secretKey]: "asdfsadf",
[delegationType]: "local|well_known|DNS_SRV",
[delegationServerName]: "name",
[delegationFederationPort]: "\"\"|325",
[delegationClientPort]: "\"\"|325",
[reverseProxy]: "nginx|caddy|apache|haproxy|other|none",
[tls]: "acme|tls|reverseproxy",
[testingCertPaths]: true,
[uploadingCerts]: true,
[certPathInvalid]: true,
[certKeyPathInvalid]: true,
[tlsCertPath]: "sadfaf",
[tlsCertKeyPath]: "sdfasdf",
[tlsCertFile]: "sadfa;dlf;sad;fkla;sdlfjkas;dlfkjas;dflkja;sdfkljadf ------",
[tlsCertKeyFile]: "sadfa;dlf;sad;fkla;sdlfjkas;dlfkjas;dflkja;sdfkljadf ------",
[verifyingPorts]: true,
[synapseFederationPortFree]: true,
[synapseClientPortFree]: true,
[synapseFederationPort]: 1234,
[synapseClientPort]: 1234,
[databaseConf]: "{dadtabaseType, databaseUsername, databasePassword, database}",
},
}
export const uiStateMapping = {
base: [
setupDone,
baseConfigChecked,
configDir,
],
[SERVER_NAME_UI]: [
servername,
secretKeyLoaded,
secretKey,
],
[STATS_REPORT_UI]: [
reportStats,
],
[KEY_EXPORT_UI]: [
],
[DELEGATION_OPTIONS_UI]: [
delegationType,
delegationServerName,
delegationClientPort,
delegationFederationPort,
],
[TLS_UI]: [
tls,
reverseProxy,
testingCertPaths,
uploadingCerts,
certPathInvalid,
certKeyPathInvalid,
tlsCertPath,
tlsCertKeyPath,
tlsCertFile,
tlsCertKeyFile,
],
[PORT_SELECTION_UI]: [
verifyingPorts,
synapseClientPort,
synapseFederationPort,
synapseClientPortFree,
synapseFederationPortFree,
],
[DATABASE_UI]: [
databaseConf,
],
[COMPLETE_UI]: [
],
}
@@ -0,0 +1,35 @@
// Setup
export const SERVER_NAME_UI = "server_name_ui";
export const STATS_REPORT_UI = "stats_report_ui";
export const KEY_EXPORT_UI = "key_export_ui";
export const DELEGATION_OPTIONS_UI = "delegation_options_ui";
export const TLS_UI = "tls_ui";
export const PORT_SELECTION_UI = "port_selection_ui";
export const REVERSE_PROXY_TEMPLATE_UI = "reverse_proxy_tamplate_ui";
export const DELEGATION_TEMPLATE_UI = "delegation_tamplate_ui";
export const DATABASE_UI = "database_ui";
export const COMPLETE_UI = "complete_ui";
export const DONE_UI = "done_ui";
// Setup order
export const SETUP_ORDER = [
SERVER_NAME_UI,
STATS_REPORT_UI,
KEY_EXPORT_UI,
DELEGATION_OPTIONS_UI,
TLS_UI,
PORT_SELECTION_UI,
DATABASE_UI,
COMPLETE_UI,
DONE_UI,
];
// Config
export const CONFIG_SELECTION_UI = "config_selection_ui";
// Loading screen:
export const LOADING_UI = "loading_ui";
// Error screen:
export const ERROR_UI = "error_ui";
@@ -0,0 +1,25 @@
export default ({
delegationFedPort,
delegationClientPort,
fedPort,
clientPort,
synapseServerName,
}) => `
<VirtualHost *:${delegationClientPort}>
SSLEngine on
ServerName ${synapseServerName};
AllowEncodedSlashes NoDecode
ProxyPass /_matrix http://127.0.0.1:${clientPort}/_matrix nocanon
ProxyPassReverse /_matrix http://127.0.0.1:${clientPort}/_matrix
</VirtualHost>
<VirtualHost *:${delegationFedPort}>
SSLEngine on
ServerName ${synapseServerName};
AllowEncodedSlashes NoDecode
ProxyPass /_matrix http://127.0.0.1:${fedPort}/_matrix nocanon
ProxyPassReverse /_matrix http://127.0.0.1:${fedPort}/_matrix
</VirtualHost>
`
@@ -0,0 +1,17 @@
export default ({
delegationFedPort,
delegationClientPort,
fedPort,
clientPort,
synapseServerName,
}) => `${synapseServerName}:${delegationClientPort} {
proxy /_matrix http://localhost:${clientPort} {
transparent
}
}
${synapseServerName}:${delegationFedPort} {
proxy / http://localhost:${fedPort} {
transparent
}
}`
@@ -0,0 +1,13 @@
/* eslint-disable max-len */
export default ({
synapseServerName,
delegationClientPort,
}) => `{
"m.homeserver": {
"base_url": "https://${synapseServerName}${delegationClientPort ? `:${delegationClientPort}` : ""}"
},
}`
// TODO: Maybe include this?
// "m.identity_server": {
// "base_url": "https://identity.example.com"
// },
@@ -0,0 +1,9 @@
/* eslint-disable max-len */
export default ({
delegationFedPort,
delegationClientPort,
fedPort,
clientPort,
serverName,
synapseServerName,
}) => `_matrix._tcp.${serverName} 3600 IN SRV 10 5 ${delegationClientPort} ${synapseServerName}`
@@ -0,0 +1,6 @@
export default ({
synapseServerName,
delegationSynapsePort,
}) => `{
"m.server": "${synapseServerName}:${delegationSynapsePort}"
}`
@@ -0,0 +1,51 @@
/* eslint-disable max-len */
export default ({
delegationFedPort,
delegationClientPort,
fedPort,
clientPort,
synapseServerName,
}) => {
if (fedPort == clientPort) {
return `frontend https
bind :::${delegationClientPort} v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2,http/1.1
# Matrix client traffic
acl matrix-host hdr(host) -i ${synapseServerName}
acl matrix-path path_beg /_matrix
use_backend matrix if matrix-host matrix-path
frontend matrix-federation
bind :::${delegationFedPort} v4v6 ssl crt /etc/ssl/haproxy/<your_tls_cert>.pem alpn h2,http/1.1
default_backend matrix
backend matrix
server matrix 127.0.0.1:${fedPort}
`
} else {
return `frontend https
bind:::${delegationClientPort} v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2, http / 1.1
# Matrix client traffic
acl matrix-host hdr(host) -i ${synapseServerName}
acl matrix-path path_beg /_matrix
use_backend matrix-client if matrix-host matrix-path
frontend matrix - federation
bind::: ${delegationFedPort} v4v6 ssl crt /etc/ssl/haproxy/<your_tls_cert>.pem alpn h2,http/1.1
default_backend matrix
backend matrix
server matrix 127.0.0.1:${fedPort}
backend matrix-client 127.0.0.1:${clientPort}`
}
}
@@ -0,0 +1,27 @@
import React from 'react';
export default ({
delegationFedPort,
delegationClientPort,
fedPort,
clientPort,
synapseServerName,
}) => `listen {delegationClientPort} ssl;
listen [::]:${delegationClientPort} ssl;
server_name ${synapseServerName};
location /_matrix {
proxy_pass http://localhost:${clientPort};
proxy_set_header X-Forwarded-For $remote_addr;
}
}
server {
listen ${delegationFedPort} ssl default_server;
listen [::]:${delegationFedPort} ssl default_server;
server_name ${synapseServerName};
location / {
proxy_pass http://localhost:${fedPort};
proxy_set_header X-Forwarded-For $remote_addr;
}
}`
+154
View File
@@ -0,0 +1,154 @@
/* eslint-disable camelcase */
import { TLS_TYPES, REVERSE_PROXY_TYPES, DATABASE_TYPES } from '../actions/constants';
import { CONFIG_LOCK } from '../api/constants';
const listeners = config => {
const listeners = [];
if (config.tls == TLS_TYPES.REVERSE_PROXY) {
listeners.push({
port: config.synapseFederationPort,
tls: false,
bind_addresses: ['::1', '127.0.0.1'],
type: "http",
x_forwarded: true,
resources: [{
names: ["federation"],
compress: false,
}],
});
} else {
listeners.push({
port: config.synapseFederationPort,
tls: true,
type: "http",
resources: [{
names: ["federation"],
}],
});
}
if (config.synapseClientPort == config.synapseFederationPort) {
listeners[0].resources[0].names.push("client");
} else if (config.tls == TLS_TYPES.REVERSE_PROXY) {
listeners.push({
port: config.synapseClientPort,
tls: false,
bind_addresses: ['::1', '127.0.0.1'],
type: "http",
x_forwarded: true,
resources: [{
names: ["client"],
compress: false,
}],
});
} else {
listeners.push({
port: config.synapseClientPort,
tls: true,
type: "http",
resources: [{
names: ["client"],
}],
});
}
return { listeners: listeners };
}
const tlsPaths = config => {
if (config.tls == TLS_TYPES.TLS) {
return {
tls_certificate_path: config.tlsCertPath,
tls_private_key_path: config.tlsCertKeyPath,
}
} else if (config.tls == TLS_TYPES.ACME) {
return {
tls_certificate_path:
config.configDir + "/" + config.servername + ".tls.cert",
tls_private_key_path:
config.configDir + "/" + config.servername + ".tls.key",
}
} else {
return {}
}
}
const acme = config => {
if (config.tls == TLS_TYPES.ACME) {
return {
acme_domain: config.delegationServerName ?
config.delegationServerName :
config.servername,
}
} else {
return {}
}
}
const database = config => {
if (config.databaseType == DATABASE_TYPES.SQLITE3) {
return {
database: {
name: config.databaseType,
}
}
} else {
return {
database: {
name: config.databaseType,
args: {
user: config.databaseUsername,
password: config.databasePassword,
host: config.databaseHost,
database: config.database,
}
}
}
}
}
export const baseConfigToSynapseConfig = config => {
const conf = {
server_name: config.servername,
report_stats: config.reportStats,
...database(config),
...listeners(config),
...tlsPaths(config),
...acme(config),
[CONFIG_LOCK]: true,
}
console.log(conf)
return conf
}
@@ -0,0 +1,38 @@
@mixin rippler {
position: relative;
overflow: hidden;
transform: translate3d(0, 0, 0);
&:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-position: 50%;
transform: scale(10, 10);
opacity: 0;
transition: transform .5s, opacity 1s;
}
&:active:after {
transform: scale(0, 0);
opacity: .3;
transition: 0s;
}
}
@mixin dropshadowed {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
&:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
}
}
File diff suppressed because one or more lines are too long
+243
View File
@@ -0,0 +1,243 @@
@import './themes.scss';
@import './animations.scss';
@import './bootstrap.min.css';
@mixin theme {
@include dark;
}
// Type selectors (Parents)
html {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1.6rem;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
@include theme;
background-color: $primary;
color: $font;
margin: auto;
width: 48rem;
max-width: 90%;
text-align: center;
}
// Type selectors (Children)
a {
@include theme;
color: $link;
text-decoration: none;
font-size: 0.6rem;
line-height: 0.8rem;
}
a:hover {
color: $link;
text-decoration: none;
}
button {
@include rippler;
border-radius: 5rem;
font-size: 0.8rem;
padding: 0.4rem 1.2rem;
color: $font;
background-color: $tertiary;
border: none;
display: inline-block;
text-transform: capitalize;
font-style: bold;
color: $primary;
}
button:hover {
background-color: $link;
}
button[disabled] {
background-color: darken($secondary, 8%);
color: lighten($font, 50%);
}
h1 {
@include theme;
font-size: 1rem;
margin: 1rem;
}
input {
padding: 0.4rem;
font-size: 0.6rem;
border-style: none;
background: white;
border-radius: 4rem;
color: $font;
padding: 0.4rem 1.2rem;
margin-bottom: 1rem;
}
input[type=checkbox] {
margin-right: 0.4rem;
}
label {
font-size: 0.6rem;
font-weight: 600;
}
p {
font-size: 0.6rem;
line-height: 0.8rem;
}
pre {
background: white;
border-radius: 0.4rem;
color: $font;
padding: 1rem;
font-size: 0.6rem;
text-align: left;
text-decoration: none;
}
select {
font-size: 0.7rem;
margin-bottom: 1.3rem;
&:after {
content: ;
transform: rotate(90deg);
position: absolute;
}
}
// Class selectors
.active-card-header {
cursor: pointer;
}
.baseintro {
padding: 2rem 1rem;
}
.blockwrapper {
display: block;
}
.buttonDisplay {
display: flex;
flex-direction: row;
justify-content: space-evenly;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.buttonGroup {
margin-top: 1rem;
margin-bottom: 1rem;
}
.card {
text-align: left;
border-style: none;
background: $secondary;
margin-bottom: 8px;
}
.card-header {
font-size: 0.8rem;
border-style: none;
}
.card-header-tabs {
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
.chevron {
float:right;
}
.inactive-card-header {
cursor: not-allowed;
}
.inputButton {
margin-left: 0.6rem;
font-size: 0.6rem;
}
.inlineError {
color: $error;
font-size: 0.6rem;
line-height: 0.8rem;
span {
margin-left: 1rem;
}
}
.invalid {
border-color: $error;
}
.invalidInput {
border-color: red;
}
.keyDisplay {
word-wrap: break-word;
}
.logo {
position: absolute;
top:0;
right: 0;
margin: 0.5rem;
}
.nav-tabs .nav-link.active {
border-color: transparent;
background-color: $smoke;
}
.nav-tabs .nav-link:hover {
border-color: transparent;
background-color: $smoke;
}
.or {
font-size: 0.6rem;
margin-left: 0.4rem;
margin-right: 0.4rem;
}
.redButton {
background-color: red;
}
.servername {
position: absolute;
margin-top: 0.5rem;
margin-left: 0.5rem;
color: darken(silver, 20%);
}
.setupCompleteTitle {
margin-left: 0;
margin-top: 0;
}
.tab-content {
background-color: $smoke;
padding: 1rem;
}
@@ -0,0 +1,10 @@
@mixin dark {
$primary: #ffffff !global;
$secondary: #f4f4f4 !global;
$tertiary: #3b444b !global;
$smoke: #ececec !global;
$font: #333 !global;
$highlight: #4aeff0 !global;
$link: #0098d4 !global;
$error: #e32017 !global;
}
@@ -0,0 +1,73 @@
import 'webpack';
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import HtmlWebpackTagsPlugin from 'html-webpack-tags-plugin';
export default {
entry: ['./src/js/index.jsx', './src/scss/main.scss', './src/scss/bootstrap.min.css'],
watch: true,
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
resolve: {
extensions: ['.js', '.jsx', '.css'],
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
},
],
},
{
test: /\.css$/,
use: [
{
loader: 'file-loader',
options: {
name: 'css/[name].css',
},
},
],
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
hash: true,
filename: __dirname + '/dist/index.html',
title: 'Topology - The synapse configuration tool',
}),
new HtmlWebpackTagsPlugin({
tags: ['css/bootstrap.min.css'],
}),
],
};