├── .gitattributes ├── .github └── workflows │ ├── bump-version.yml │ ├── clearfog-ubuntu-iso.yml │ ├── codeql.yml │ ├── docker-image.yml │ ├── ios-build.yml │ ├── pi-ubuntu-iso.yml │ ├── sbom-build.yml │ ├── sbom-docker.yml │ └── test-ui.yml ├── .gitignore ├── LICENSE ├── README.md ├── RELEASE-NOTES.md ├── api ├── Dockerfile ├── code │ ├── alerts.go │ ├── alerts_test.go │ ├── api.go │ ├── auth.go │ ├── darwin.go │ ├── dhcp.go │ ├── dns.go │ ├── firewall.go │ ├── go.mod │ ├── go.sum │ ├── hostapd_template.conf │ ├── interfaces.go │ ├── linux.go │ ├── plugins.go │ ├── radios.go │ ├── traffic.go │ ├── uplink.go │ └── ws.go └── scripts │ ├── generate-certificate.sh │ ├── iwdev.py │ ├── iwlist.py │ ├── iwreg.py │ ├── rand_oui_prefixes │ ├── startup.sh │ └── update-zone-to-group.sh ├── api_sample_plugin ├── Dockerfile ├── README.md ├── code │ ├── go.mod │ ├── go.sum │ └── sample_plugin.go ├── docker-compose.yml ├── plugin.json └── scripts │ └── startup.sh ├── base ├── Dockerfile ├── build.sh ├── docker_nftables_setup.sh ├── docker_nftables_setup_bridge.sh ├── run_test.sh ├── scripts │ ├── accounting.sh │ ├── nft_rules.sh │ ├── perftune.sh │ └── startup.sh ├── setup-sdcard.sh ├── setup.sh ├── template_configs │ ├── .gitignore │ ├── auth │ │ └── auth_tokens.json │ ├── base │ │ ├── alerts.json │ │ ├── api.json │ │ ├── config.sh │ │ ├── dhcp.json │ │ ├── firewall.json │ │ ├── interfaces.json │ │ ├── lanip │ │ └── virtual-config.sh │ ├── devices │ │ ├── devices.json │ │ └── groups.json │ ├── dhcp │ │ └── .gitignore │ ├── dns │ │ └── Corefile │ ├── mon │ │ └── telegraf.conf │ ├── ppp │ │ ├── chap-secrets │ │ ├── peers │ │ │ ├── .gitignore │ │ │ └── peer_sample │ │ └── vars.sh │ ├── scripts │ │ ├── gen_coredhcp_yaml.sh │ │ └── gen_watchdog.sh │ ├── watchdog │ │ └── .gitignore │ ├── wifi │ │ ├── hostapd_template.conf │ │ ├── sae_passwords │ │ └── wpa2pskfile │ └── wireguard │ │ └── wg0.conf └── verify_host_setup.sh ├── build_docker_compose.sh ├── db ├── Dockerfile ├── README.md ├── code │ ├── Makefile │ ├── boltapi.go │ ├── cmd │ │ └── boltapi │ │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── sweep.go └── scripts │ └── startup.sh ├── dhcp ├── .gitignore ├── Dockerfile └── scripts │ ├── client.sh │ ├── dhclient-gateway-hook.sh │ └── startup.sh ├── dns ├── .gitignore ├── Dockerfile └── scripts │ └── startup.sh ├── docker-compose-test.yml ├── docker-compose-virt.yml ├── docker-compose.yml ├── dyndns ├── Dockerfile ├── code │ ├── dyndns_plugin.go │ ├── go.mod │ └── go.sum └── docker-compose.yml ├── flowgather ├── Dockerfile ├── LICENSE ├── build.sh ├── code │ ├── go.mod │ ├── go.sum │ └── main.go └── scripts │ └── startup.sh ├── frontend ├── .bundle │ └── config ├── .dockerignore ├── .gitignore ├── .prettierrc.json ├── Dockerfile ├── Dockerfile-helper ├── README.md ├── app.json ├── babel-plugin-macros.config.js ├── babel.config.js ├── index.js ├── ios │ ├── Podfile │ ├── Podfile.lock │ ├── spr.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ ├── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── xcuserdata │ │ │ │ └── po.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── spr.xcscheme │ ├── spr.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── spr │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── logo-1.png │ │ │ │ ├── logo-2.png │ │ │ │ ├── logo-3.png │ │ │ │ ├── logo-4.png │ │ │ │ ├── logo-5.png │ │ │ │ ├── logo-6.png │ │ │ │ ├── logo-7.png │ │ │ │ ├── logo-8.png │ │ │ │ └── logo.png │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ ├── main.m │ │ └── sprRelease.entitlements │ ├── sprTests │ │ ├── Info.plist │ │ └── sprTests.m │ └── update-version.sh ├── jsconfig.json ├── macos │ ├── .gitignore │ ├── Podfile │ ├── Podfile.lock │ ├── supernetworks-SPR-ui-iOS │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ ├── supernetworks-SPR-ui-macOS │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ ├── ViewController.h │ │ ├── ViewController.m │ │ ├── main.m │ │ └── supernetworks-SPR-ui.entitlements │ ├── supernetworks-SPR-ui.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── supernetworks-SPR-ui-iOS.xcscheme │ │ │ └── supernetworks-SPR-ui-macOS.xcscheme │ └── supernetworks-SPR-ui.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ └── po.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── metro.config.js ├── package.json ├── public │ ├── apple-icon.png │ ├── bg.jpg │ ├── favicon.png │ ├── font-awesome.min.css │ ├── fonts │ │ ├── ATTRIBUTION │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── index.html │ └── manifest.json ├── src │ ├── App.css │ ├── App.js │ ├── App.web.js │ ├── AppContext.js │ ├── DeviceInfo.ios.js │ ├── DeviceInfo.js │ ├── IconUtils.js │ ├── Notifications.ios.js │ ├── Notifications.js │ ├── Theme.js │ ├── __tests__ │ │ ├── API.js │ │ ├── AlertUtil.js │ │ ├── App.js │ │ ├── ClientSelect.js │ │ ├── DNS.js │ │ ├── DeviceList.js │ │ ├── Pfw.js │ │ ├── Setup.js │ │ ├── Widgets.js │ │ └── Wireguard.js │ ├── api │ │ ├── API.js │ │ ├── Alerts.js │ │ ├── Auth.js │ │ ├── CoreDNS.js │ │ ├── DNS.js │ │ ├── Db.js │ │ ├── Device.js │ │ ├── Dyndns.js │ │ ├── Firewall.js │ │ ├── Group.js │ │ ├── Logs.js │ │ ├── MockAPI.js │ │ ├── Multicast.js │ │ ├── Nfmap.js │ │ ├── Notifications.js │ │ ├── Pfw.js │ │ ├── Plugin.js │ │ ├── Traffic.js │ │ ├── WebSocket.js │ │ ├── Wifi.js │ │ ├── Wireguard.js │ │ ├── index.js │ │ ├── mesh.js │ │ └── mock │ │ │ └── alertbucket.js │ ├── assets │ │ ├── fonts │ │ │ ├── nucleo-icons.eot │ │ │ ├── nucleo-icons.ttf │ │ │ ├── nucleo-icons.woff │ │ │ └── nucleo-icons.woff2 │ │ └── img │ │ │ └── bg │ │ │ ├── .DS_Store │ │ │ └── bg.jpg │ ├── components │ │ ├── Accordion.js │ │ ├── ActionSheet.js │ │ ├── Alerts │ │ │ ├── AddAlert.js │ │ │ ├── AlertChart.js │ │ │ ├── AlertChart.web.js │ │ │ ├── AlertListItem.js │ │ │ ├── AlertTemplates.js │ │ │ ├── AlertUtil.js │ │ │ ├── EventTimelineChart.js │ │ │ └── EventTimelineChart.web.js │ │ ├── Auth │ │ │ ├── AddAuthToken.js │ │ │ ├── AuthTokenList.js │ │ │ ├── OTPSettings.js │ │ │ ├── OTPValidate.js │ │ │ └── WebAuthn.js │ │ ├── ClientSelect.js │ │ ├── ColorPicker.js │ │ ├── DNS │ │ │ ├── DNSAddBlocklist.js │ │ │ ├── DNSAddOverride.js │ │ │ ├── DNSBlocklist.js │ │ │ ├── DNSBlocklistSettings.js │ │ │ ├── DNSChart.js │ │ │ ├── DNSChart.web.js │ │ │ ├── DNSImportOverride.js │ │ │ ├── DNSLogHistoryList.js │ │ │ ├── DNSLogList.js │ │ │ ├── DNSOverrideList.ios.js │ │ │ ├── DNSOverrideList.js │ │ │ └── DNSOverrideListItem.js │ │ ├── Dashboard │ │ │ ├── DNSMetricsWidgets.js │ │ │ ├── HealthCheck.js │ │ │ ├── Intro.js │ │ │ ├── ServicesWidgets.js │ │ │ ├── StatsChartWidget.js │ │ │ ├── StatsChartWidget.web.js │ │ │ ├── StatsWidget.js │ │ │ ├── TrafficWidgets.js │ │ │ ├── WifiWidgets.js │ │ │ └── WireguardWidgets.js │ │ ├── DatePicker.js │ │ ├── DatePicker.web.js │ │ ├── DateRange.js │ │ ├── Devices │ │ │ ├── AddDevice.js │ │ │ ├── Device.js │ │ │ ├── DeviceExpiry.js │ │ │ ├── DeviceItem.js │ │ │ ├── DeviceList.ios.js │ │ │ ├── DeviceList.js │ │ │ ├── DeviceQRCode.js │ │ │ └── EditDevice.js │ │ ├── Firewall │ │ │ ├── AddBlock.js │ │ │ ├── AddContainerInterfaceRule.js │ │ │ ├── AddEndpoint.js │ │ │ ├── AddForward.js │ │ │ ├── AddForwardBlock.js │ │ │ ├── AddMulticastPort.js │ │ │ ├── AddOutputBlock.js │ │ │ ├── AddServicePort.js │ │ │ ├── BlockList.js │ │ │ ├── ContainerInterfaceRulesList.js │ │ │ ├── EndpointList.js │ │ │ ├── ForwardBlockList.js │ │ │ ├── ForwardList.js │ │ │ ├── ICMP.js │ │ │ ├── MDNSAdvertise.js │ │ │ ├── MulticastPorts.js │ │ │ ├── OutputBlockList.js │ │ │ └── UpstreamServicesList.js │ │ ├── Flow │ │ │ ├── AddFlowCard.js │ │ │ ├── EditFlow.js │ │ │ ├── EditItem.js │ │ │ ├── Flow.js │ │ │ ├── FlowCard.js │ │ │ ├── FlowCards.js │ │ │ ├── FlowList.js │ │ │ ├── Token.js │ │ │ └── Utils.js │ │ ├── Footer │ │ │ └── Footer.js │ │ ├── Form │ │ │ └── ProtocolRadio.js │ │ ├── Groups │ │ │ └── GroupListing.js │ │ ├── IPInfo.js │ │ ├── IconItem.js │ │ ├── IconPicker.js │ │ ├── InputSelect.js │ │ ├── List │ │ │ ├── AddButton.js │ │ │ ├── ListHeader.js │ │ │ ├── ListItem.js │ │ │ └── index.js │ │ ├── Logs │ │ │ ├── FilterInputSelect.js │ │ │ ├── FilterSelect.js │ │ │ ├── LogList.js │ │ │ ├── LogListDb.js │ │ │ └── LogListItem.js │ │ ├── Menu.js │ │ ├── Mesh │ │ │ └── AddLeafRouter.js │ │ ├── ModalConfirm.js │ │ ├── ModalForm.js │ │ ├── Navbars │ │ │ ├── AdminNavbar.js │ │ │ └── RouteJump.js │ │ ├── Notifications │ │ │ └── AddNotification.js │ │ ├── Pagination.js │ │ ├── Plugins │ │ │ ├── AddPlugin.js │ │ │ ├── CustomPlugin.js │ │ │ ├── CustomPlugin.web.js │ │ │ ├── InstallPlugin.js │ │ │ └── PluginList.js │ │ ├── Select.js │ │ ├── Select.web.js │ │ ├── Setup │ │ │ └── AddDevice.js │ │ ├── Sidebar │ │ │ └── Sidebar.js │ │ ├── SwipeListView │ │ │ ├── SwipeListView.js │ │ │ ├── SwipeRow.js │ │ │ └── index.js │ │ ├── SyntaxHighlighter.js │ │ ├── System │ │ │ └── Release.js │ │ ├── TabView.js │ │ ├── TagItem.js │ │ ├── TagMenu.js │ │ ├── TimeSelect.js │ │ ├── Toggle.css │ │ ├── Toggle.js │ │ ├── TokenItem.js │ │ ├── Tooltip.js │ │ ├── Traffic │ │ │ ├── TimeSeries.js │ │ │ ├── TimeSeriesChart.js │ │ │ ├── TimeSeriesChart.web.js │ │ │ └── TimeSeriesList.js │ │ ├── Wifi │ │ │ ├── WifiChannelParameters.js │ │ │ ├── WifiClients.js │ │ │ ├── WifiGuestNetwork.js │ │ │ ├── WifiGuestNetworkParameters.js │ │ │ ├── WifiHostapd.js │ │ │ ├── WifiInterfaceList.js │ │ │ ├── WifiScan.js │ │ │ └── WifiSignal.js │ │ ├── Wireguard │ │ │ ├── PeerList.js │ │ │ ├── SiteVPN.js │ │ │ ├── WireguardAddPeer.js │ │ │ ├── WireguardAddSite.js │ │ │ └── WireguardConfig.js │ │ └── useSwipe.js │ ├── gluestack-ui.config.js │ ├── index.js │ ├── layouts │ │ ├── Admin.js │ │ ├── Auth.js │ │ └── Auth.web.js │ ├── routes.js │ ├── setupTests.js │ ├── test-utils.js │ ├── useAsyncStorage.js │ ├── utils.js │ └── views │ │ ├── AlertSettings.js │ │ ├── Alerts.js │ │ ├── Alerts │ │ └── AddAlert.js │ │ ├── AlertsTabView.js │ │ ├── AuthSettings.js │ │ ├── AuthValidate.js │ │ ├── ContainerNetConfiguration.js │ │ ├── CustomPlugin.js │ │ ├── DNS │ │ ├── CoreDns.js │ │ ├── DNSBlock.js │ │ ├── DNSLog.js │ │ ├── DNSLogEdit.js │ │ ├── DNSOverride.js │ │ └── DynDns.js │ │ ├── Devices │ │ ├── AddDevice.js │ │ ├── Arp.js │ │ ├── ConnectDevice.js │ │ ├── Device.js │ │ └── Devices.js │ │ ├── Events.js │ │ ├── Firewall │ │ ├── Firewall.js │ │ ├── FirewallSettings.js │ │ ├── FirewallTabView.js │ │ └── Pfw.js │ │ ├── Groups │ │ ├── Dhcp.js │ │ └── Groups.js │ │ ├── Home.js │ │ ├── LinkConfiguration │ │ ├── LANLinkConfiguration.js │ │ └── UplinkConfiguration.js │ │ ├── LinkConfigurationTabView.js │ │ ├── Logs.js │ │ ├── Mesh.js │ │ ├── Notifications.js │ │ ├── PluginDisabled.js │ │ ├── Plugins.js │ │ ├── SpeedTest.js │ │ ├── Supernetworks.js │ │ ├── System │ │ ├── Configs.js │ │ ├── Database.js │ │ ├── Docker.js │ │ ├── EditDatabase.js │ │ ├── PfwTasks.js │ │ ├── SystemInfo.js │ │ ├── SystemInfoContainers.js │ │ └── SystemInfoNetworkMisc.js │ │ ├── SystemInfoContainers.js │ │ ├── SystemInfoNetworkMisc.js │ │ ├── SystemInfoTabView.js │ │ ├── Tags.js │ │ ├── Traffic │ │ ├── SignalStrength.js │ │ ├── SignalStrength.web.js │ │ ├── Traffic.js │ │ ├── Traffic.web.js │ │ ├── TrafficList.js │ │ └── TrafficTimeSeries.js │ │ ├── Wireguard.js │ │ ├── WirelessConfiguration.js │ │ └── pages │ │ ├── Login.js │ │ └── Setup.js ├── webpack.config.js └── yarn.lock ├── installer ├── README.md ├── clearfog-arm64-image-build.sh ├── clearfog-download-image.sh ├── pi-arm64-image-build.sh ├── pi-download-image.sh ├── qemu-setup-linux.sh ├── scripts │ ├── containers.sh │ ├── go-clearfog.sh │ ├── go-pi.sh │ ├── install.sh │ ├── mount-clearfog.sh │ ├── mount.sh │ ├── pi-cross-install.sh │ ├── pi-target-install.sh │ ├── resize-clearfog.sh │ ├── resize.sh │ ├── run-scripts │ │ ├── rc-local │ │ ├── run.sh │ │ └── setup.sh │ ├── shrink.sh │ ├── spr-environment-clearfog.sh │ ├── spr-environment.sh │ ├── unmount-clearfog.sh │ └── unmount.sh ├── upstream-docker.sh └── write-img.sh ├── multicast_udp_proxy ├── Dockerfile ├── LICENSE ├── code │ ├── go.mod │ ├── go.sum │ └── multicastproxy.go ├── scripts │ ├── .gitignore │ └── startup.sh └── tbd.txt ├── packet_logs ├── Dockerfile ├── README.md ├── client-influxdb │ ├── README.md │ ├── go.mod │ ├── go.sum │ └── main.go ├── client-test │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── types.go ├── code │ ├── dns.go │ ├── go.mod │ ├── go.sum │ ├── interface_names.go │ └── main.go ├── scripts │ ├── startup.sh │ └── update-netfilter-rules.sh ├── stream-json-logs │ ├── go.mod │ ├── go.sum │ └── main.go └── ulogd.conf ├── plugin-lookup ├── Dockerfile ├── README.md ├── code │ ├── go.mod │ ├── go.sum │ ├── lookup_plugin.go │ └── lookup_plugin_test.go ├── data │ └── .gitignore └── scripts │ ├── download.sh │ ├── startup.sh │ └── test.sh ├── ppp ├── Dockerfile ├── docker-compose.yml └── scripts │ └── startup.sh ├── pull_containers.sh ├── superd ├── Dockerfile ├── code │ ├── go.mod │ ├── go.sum │ └── superd.go └── scripts │ ├── docker-compose │ ├── docker.asc │ └── startup.sh ├── tests ├── README.md ├── hostapd_wlan1.conf ├── hwsim_setup.sh ├── run-tests.sh ├── run_sta.sh ├── run_sta4.sh ├── runner │ ├── Dockerfile │ ├── build.sh │ ├── code │ │ ├── .gitignore │ │ ├── README.md │ │ ├── agent.js │ │ ├── e2e │ │ │ ├── README.md │ │ │ ├── custom_interfaces.js │ │ │ └── firewall.js │ │ ├── host │ │ │ └── stations.js │ │ ├── index.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── plus │ │ │ └── pfw.js │ │ └── test │ │ │ ├── auth.js │ │ │ ├── device.js │ │ │ ├── dhcp.js │ │ │ ├── features.js │ │ │ ├── ip.js │ │ │ ├── releases.js │ │ │ ├── vpn.js │ │ │ └── wifi.js │ └── go.sh ├── sta1 │ ├── Dockerfile │ ├── build.sh │ ├── go.sh │ ├── sta3.conf │ ├── sta3.sh │ ├── sta4.conf │ ├── sta4.sh │ └── w.conf ├── test_configs │ ├── .gitignore │ ├── base │ │ ├── api.json │ │ ├── auth_tokens.json │ │ ├── auth_users.json │ │ ├── config.sh │ │ ├── dhcp.json │ │ ├── firewall.json │ │ ├── interfaces.json │ │ ├── release_channel │ │ └── release_version │ ├── devices │ │ ├── devices.json │ │ └── groups.json │ ├── dhcp │ │ ├── .gitignore │ │ └── coredhcp.yml │ ├── dns │ │ └── Corefile │ ├── dyndns │ │ └── godyndns.json │ ├── mon │ │ └── telegraf.conf │ ├── pfw │ │ └── rules.json │ ├── ppp │ │ ├── chap-secrets │ │ └── peers │ │ │ ├── .gitignore │ │ │ └── peer_sample │ ├── scripts │ │ ├── gen_coredhcp_yaml.sh │ │ ├── gen_hostapd.sh │ │ ├── gen_multicast_startup.sh │ │ └── gen_watchdog.sh │ ├── watchdog │ │ ├── .gitignore │ │ └── watchdog.conf │ ├── wifi │ │ ├── .hostapd_wlan1.conf.swp │ │ ├── hostapd.conf.bak │ │ ├── hostapd_template.conf │ │ ├── hostapd_wlan1.conf │ │ └── hostapd_wlan1.conf.bak │ └── wireguard │ │ └── wg0.conf ├── test_start.sh └── test_stop.sh ├── virtual_install.sh ├── watchdog ├── Dockerfile └── scripts │ └── startup.sh ├── wifi_uplink ├── Dockerfile ├── docker-compose.yml └── scripts │ └── startup.sh ├── wifid-setup ├── .gitignore ├── docker-compose-test.yml └── docker-compose.yml ├── wifid ├── .gitignore ├── Dockerfile ├── code │ ├── filter_dhcp_mismatch.c │ ├── hostapd_config │ └── main.go ├── notes.txt └── scripts │ ├── action-setup.sh │ ├── action.sh │ ├── hostapd_failsafe_band1.conf │ ├── hostapd_failsafe_band2.conf │ ├── pi-setup.conf │ ├── setup.sh │ ├── startup.sh │ └── update_iface_names.sh └── wireguard ├── Dockerfile ├── README.md ├── code ├── go.mod ├── go.sum ├── run.sh └── wireguard_plugin.go └── scripts ├── down.sh ├── startup.sh ├── up.sh └── wg-json /.gitattributes: -------------------------------------------------------------------------------- 1 | frontend/src/assets/** linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/bump-version.yml: -------------------------------------------------------------------------------- 1 | name: Bump version 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | fetch-depth: '0' 14 | - name: Bump version and push tag 15 | uses: anothrNick/github-tag-action@1.67.0 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | WITH_V: true 19 | INITIAL_VERSION: 0.1.0 20 | DEFAULT_BUMP: none 21 | -------------------------------------------------------------------------------- /.github/workflows/sbom-build.yml: -------------------------------------------------------------------------------- 1 | # NOTE move this to docker build yml 2 | name: SBOM Artifact 3 | 4 | on: 5 | push: 6 | branches: [ main, dev ] 7 | # workflow_run: 8 | # workflows: [Bump version] 9 | # branches: [ main, dev ] 10 | # types: 11 | # - completed 12 | 13 | jobs: 14 | on-success: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | fetch-depth: '0' 20 | 21 | - name: Set env 22 | run: echo "RELEASE_VERSION=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV 23 | - name: Test release tag version 24 | run: echo "Release == $RELEASE_VERSION ==" 25 | 26 | - uses: anchore/sbom-action@v0 27 | with: 28 | path: ./ 29 | 30 | - uses: anchore/sbom-action/publish-sbom@v0 31 | with: 32 | sbom-artifact-match: ".*\\.spdx\\.json$" 33 | artifact-name: spr-sbom.spdx.json 34 | -------------------------------------------------------------------------------- /.github/workflows/test-ui.yml: -------------------------------------------------------------------------------- 1 | name: UI Test CI 2 | 3 | on: 4 | push: 5 | branches: [ main, dev ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build-and-test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | 16 | - name: Use Node.js 18 17 | uses: actions/setup-node@v2 18 | with: 19 | node-version: 18 20 | 21 | - name: Cache node modules 22 | uses: actions/cache@v4 23 | with: 24 | path: '**/node_modules' 25 | key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} 26 | 27 | - name: Cache Cypress binary 28 | uses: actions/cache@v4 29 | with: 30 | path: ~/.cache/Cypress 31 | key: cypress-${{ runner.os }}-cypress-${{ hashFiles('**/yarn.lock') }} 32 | 33 | - name: Install dependencies 34 | working-directory: ./frontend 35 | run: yarn install --frozen-lockfile 36 | 37 | - name: Build 38 | working-directory: ./frontend 39 | run: yarn build 40 | 41 | - name: Run tests 42 | working-directory: ./frontend 43 | run: yarn test 44 | # cypress-run: # job name 45 | # - name: Cypress test 46 | # uses: cypress-io/github-action@v2 47 | # with: 48 | # working-directory: ./frontend 49 | # start: npx serve -s build 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | configs/ 2 | state/ 3 | .github_creds 4 | .spr-setup-done 5 | node_modules 6 | -------------------------------------------------------------------------------- /api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS builder 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends nftables iproute2 netcat-traditional inetutils-ping net-tools nano ca-certificates git curl wget 5 | 6 | ARG TARGETARCH 7 | RUN wget https://dl.google.com/go/go1.24.2.linux-${TARGETARCH}.tar.gz 8 | RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.2.linux-${TARGETARCH}.tar.gz 9 | ENV PATH="/usr/local/go/bin:$PATH" 10 | 11 | 12 | RUN mkdir /code 13 | WORKDIR /code 14 | RUN git clone https://github.com/spr-networks/sprbus --depth 1 15 | WORKDIR /code/sprbus/cmd/ 16 | 17 | ARG USE_TMPFS=true 18 | RUN --mount=type=tmpfs,target=/tmpfs \ 19 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 20 | go build -ldflags "-s -w" -o /sprbus 21 | 22 | WORKDIR /code 23 | COPY code/ /code/ 24 | RUN --mount=type=tmpfs,target=/tmpfs \ 25 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 26 | go build -ldflags "-s -w" -o /api 27 | 28 | FROM ghcr.io/spr-networks/container_template:latest 29 | ENV DEBIAN_FRONTEND=noninteractive 30 | RUN apt-get update && apt-get install -y --no-install-recommends openssh-client git hostapd iw jc systemd macchanger && rm -rf /var/lib/apt/lists/* 31 | COPY scripts /scripts/ 32 | # iw list json parser 33 | RUN mkdir -p /root/.local/share/jc/jcparsers 34 | COPY scripts/iw*.py /root/.local/share/jc/jcparsers/ 35 | 36 | COPY --from=builder /api / 37 | COPY --from=builder /sprbus / 38 | ENTRYPOINT ["/scripts/startup.sh"] 39 | -------------------------------------------------------------------------------- /api/code/darwin.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | // +build darwin 3 | 4 | package main 5 | 6 | func bindToDevice(fd int, ifName string) error { 7 | // No-op on macOS 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /api/code/hostapd_template.conf: -------------------------------------------------------------------------------- 1 | ctrl_interface=/state/wifi/control_ 2 | country_code=US 3 | interface= 4 | ssid= 5 | hw_mode=a 6 | ieee80211d=1 7 | ieee80211n=1 8 | ieee80211ac=1 9 | ieee80211h=1 10 | #wifi 6 11 | ieee80211ax=0 12 | he_su_beamformer=0 13 | he_su_beamformee=0 14 | he_mu_beamformer=0 15 | #for mixed mode need w=1 16 | ieee80211w=1 17 | beacon_prot=1 18 | wmm_enabled=1 19 | preamble=1 20 | # awus036acm and netgear a6210 supported features 21 | ht_capab=[LDPC][HT40+][HT40-][GF][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1] 22 | vht_capab=[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][MAX-A-MPDU-LEN-EXP3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN] 23 | vht_oper_chwidth=1 24 | channel=36 25 | vht_oper_centr_freq_seg0_idx=42 26 | auth_algs=1 27 | wpa=2 28 | wpa_key_mgmt=WPA-PSK WPA-PSK-SHA256 SAE 29 | rsn_pairwise=CCMP CCMP-256 30 | 31 | # Security parameters 32 | 33 | # Isolate stations and per-station group keys 34 | ap_isolate=1 35 | multicast_to_unicast=1 36 | tdls_prohibit=1 37 | 38 | # Mitigate krack attack 39 | wpa_disable_eapol_key_retries=1 40 | 41 | # For PMKID leaks in wpa2, any hardening to do here? Besides not using fast roaming? 42 | # no rsn_preauth, no 11r/ft-psk 43 | 44 | # VLAN 45 | per_sta_vif=1 46 | 47 | # Support H2E and hunting and pecking 48 | sae_pwe=2 49 | 50 | # Allow multiple wildcards 51 | sae_track_password=50 52 | 53 | # Passwords 54 | 55 | wpa_psk_file=/configs/wifi/wpa2pskfile 56 | sae_psk_file=/configs/wifi/sae_passwords 57 | -------------------------------------------------------------------------------- /api/code/linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package main 5 | 6 | import ( 7 | "syscall" 8 | ) 9 | 10 | func bindToDevice(fd int, ifName string) error { 11 | return syscall.BindToDevice(fd, ifName) 12 | } 13 | -------------------------------------------------------------------------------- /api/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -a 3 | . /configs/base/config.sh 4 | MACHINE_ID=$(ls -1 /var/log/journal|head -1) 5 | echo $MACHINE_ID > /etc/machine-id 6 | 7 | # enable ssl if we have a certificate 8 | if [ -f "/configs/auth/www-api.key" ] && [ -f "/configs/auth/www-api.crt" ]; then 9 | export API_SSL_PORT=443 10 | 11 | # fix update old cert <= 0.3.12 12 | if [ -f "/configs/auth/cert/www-api-inter.crt" ]; then 13 | echo "+ old cert found, generate new with scripts/generate-certificate.sh" 14 | rm -f /configs/auth/cert/www-api-inter.* 15 | /scripts/generate-certificate.sh 16 | fi 17 | 18 | # check if cert expire soon 19 | #/scripts/generate-certificate.sh status > /dev/null 20 | #test $? -eq 0 || /scripts/generate-certificate.sh 21 | fi 22 | 23 | /api 24 | -------------------------------------------------------------------------------- /api/scripts/update-zone-to-group.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NOTE 4 | # after running this restart at least base + api containers and 5 | # reconnect to wifi to refresh the nft maps 6 | 7 | D="/" 8 | 9 | # if run outside of container 10 | if [ ! -d "$D/configs" ]; then 11 | SCRIPT_DIR=$(dirname "$(readlink -f "$0")") 12 | D="${SCRIPT_DIR}/../.." 13 | fi 14 | 15 | if [ -f "$D/configs/devices/zones.json" ]; then 16 | echo "+ updating zone files" 17 | mv $D/configs/devices/zones.json $D/configs/devices/zones.json.bak-upgrade 18 | mv $D/configs/devices/devices.json $D/configs/devices/devices.json.bak-upgrade 19 | cat $D/configs/devices/zones.json.bak-upgrade | sed 's/ZoneTags/GroupTags/g' > $D/configs/devices/groups.json 20 | cat $D/configs/devices/devices.json.bak-upgrade | sed 's/Zones/Groups/g' > $D/configs/devices/devices.json 21 | else 22 | exit 23 | fi 24 | -------------------------------------------------------------------------------- /api_sample_plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS builder 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends nftables iproute2 netcat-traditional inetutils-ping net-tools nano ca-certificates git curl wget 5 | RUN mkdir /code 6 | WORKDIR /code 7 | ARG TARGETARCH 8 | RUN wget https://dl.google.com/go/go1.24.2.linux-${TARGETARCH}.tar.gz 9 | RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.2.linux-${TARGETARCH}.tar.gz 10 | ENV PATH="/usr/local/go/bin:$PATH" 11 | COPY code/ /code/ 12 | 13 | ARG USE_TMPFS=true 14 | RUN --mount=type=tmpfs,target=/tmpfs \ 15 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 16 | go build -ldflags "-s -w" -o /api_sample_plugin /code/sample_plugin.go 17 | 18 | 19 | FROM ghcr.io/spr-networks/container_template:latest 20 | ENV DEBIAN_FRONTEND=noninteractive 21 | #RUN apt-get update && apt-get install -y --no-install-recommends tcpdump && rm -rf /var/lib/apt/lists/* 22 | COPY scripts /scripts/ 23 | COPY --from=builder /api_sample_plugin / 24 | ENTRYPOINT ["/scripts/startup.sh"] 25 | -------------------------------------------------------------------------------- /api_sample_plugin/README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | This is a code sample for how to create a plugin for SPR. 4 | Also see https://github.com/spr-networks/spr-sample-plugin for a plugin with a UI 5 | 6 | Check the [api docs](https://www.supernetworks.org/pages/docs/apis/overview#api-plugins) for information about how plugins work 7 | 8 | ## Buiding & Running 9 | 10 | ``` 11 | docker compose build 12 | export SUPERDIR=/home/spr/super/ #path where super is 13 | docker compose up 14 | ``` 15 | 16 | ## API Calls 17 | 18 | The plugin can export API extensions over a unix socket to the API. 19 | 20 | ## Configuring the plugin to auto-start 21 | 22 | 1. configure the plugin in the UI or 23 | 2. Update the `configs/base/custom_compose_paths.json` to add the plugin. It is expected to be relative from the `super/` directory, for example `plugins/test/docker-comopse.yml` 24 | 25 | 26 | ## SPRBus notes 27 | 28 | SPRBus is our event bus where the API can send events. 29 | dcThe sample includes commented code for how to use it. 30 | -------------------------------------------------------------------------------- /api_sample_plugin/code/go.mod: -------------------------------------------------------------------------------- 1 | module sample_plugin 2 | 3 | go 1.17 4 | 5 | require github.com/gorilla/mux v1.8.0 6 | -------------------------------------------------------------------------------- /api_sample_plugin/code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 2 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 3 | -------------------------------------------------------------------------------- /api_sample_plugin/docker-compose.yml: -------------------------------------------------------------------------------- 1 | x-logging: 2 | &default-logging 3 | driver: journald 4 | 5 | x-labels: 6 | &default-labels 7 | org.supernetworks.ci: ${CI:-false} 8 | org.supernetworks.version: ${RELEASE_VERSION:-latest}${RELEASE_CHANNEL:-} 9 | 10 | services: 11 | api_sample_plugin: 12 | container_name: superapi_sample_plugin 13 | network_mode: ${NETWORK_MODE:-host} 14 | build: 15 | context: . 16 | labels: *default-labels 17 | logging: *default-logging 18 | volumes: 19 | - /etc/timezone:/etc/timezone:ro 20 | - /etc/localtime:/etc/localtime:ro 21 | - "${SUPERDIR}./state/plugins/api_sample_plugin:/state/plugins/api_sample_plugin" 22 | - "${SUPERDIR}./state/public/:/state/public/:ro" 23 | # - "${SUPERDIR}./state/api/:/state/api/:ro" #uncomment me for SPRBUS access 24 | -------------------------------------------------------------------------------- /api_sample_plugin/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "Name": "spr-sample-plugin", 3 | "ComposeFilePath": "plugins/user/spr-sample-plugin/docker-compose.yml", 4 | "UnixPath": "/state/plugins/spr-sample-plugin/socket", 5 | "URI": "spr-sample-plugin", 6 | "HasUI": false, 7 | "SandboxedUI": false, 8 | "InstallTokenPath": "/configs/plugins/spr-sample-plugin/api-token", 9 | "Enabled": true 10 | } 11 | -------------------------------------------------------------------------------- /api_sample_plugin/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . /configs/base/config.sh 3 | /api_sample_plugin 4 | -------------------------------------------------------------------------------- /base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/spr-networks/container_template:latest 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update && apt-get install -y --no-install-recommends conntrack iptables isc-dhcp-client && rm -rf /var/lib/apt/lists/* 4 | COPY scripts /scripts 5 | 6 | ENTRYPOINT ["/scripts/startup.sh"] 7 | -------------------------------------------------------------------------------- /base/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | NAME=superbase 3 | docker build . -t ${NAME} 4 | -------------------------------------------------------------------------------- /base/docker_nftables_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Set up nftables for docker 3 | iptables --flush 4 | iptables -t nat --flush 5 | iptables --delete-chain 6 | iptables -t nat --delete-chain 7 | 8 | iptables-legacy --flush 9 | iptables-legacy -t nat --flush 10 | iptables-legacy --delete-chain 11 | iptables-legacy -t nat --delete-chain 12 | 13 | . configs/base/config.sh 14 | 15 | nft flush ruleset 16 | nft -f - << EOF 17 | 18 | table inet filter { 19 | chain INPUT { 20 | type filter hook input priority 0; policy accept; 21 | counter jump F_EST_RELATED 22 | 23 | # Input rules 24 | iif lo counter accept 25 | } 26 | 27 | chain FORWARD { 28 | type filter hook forward priority 0; policy drop; 29 | 30 | counter jump F_EST_RELATED 31 | iif $DOCKERIF oifname $WANIF ip saddr $DOCKERNET counter accept 32 | 33 | # MSS clamping to handle upstream MTU limitations 34 | tcp flags syn tcp option maxseg size set rt mtu 35 | 36 | } 37 | 38 | chain OUTPUT { 39 | type filter hook output priority 0; policy accept 40 | } 41 | 42 | chain F_EST_RELATED { 43 | ip protocol udp ct state related,established counter accept 44 | ip protocol tcp ct state related,established counter accept 45 | ip protocol icmp ct state related,established counter accept 46 | } 47 | 48 | } 49 | 50 | table inet nat { 51 | chain POSTROUTING { 52 | type nat hook postrouting priority 100; policy accept; 53 | oifname $WANIF counter masquerade 54 | } 55 | } 56 | 57 | EOF 58 | -------------------------------------------------------------------------------- /base/run_test.sh: -------------------------------------------------------------------------------- 1 | NAME=superbase 2 | docker rm ${NAME} 3 | docker run --name=${NAME} --platform=linux/arm --network=host --privileged --hostname=${NAME} -it ${NAME} 4 | -------------------------------------------------------------------------------- /base/scripts/perftune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Do a little more work than irqbalance. 4 | 5 | # The PI board has USB stuck on cpu 0 by default. Since the wifi dongle is stuck there, 6 | # Move receive/xmit queues off of it 7 | 8 | # e -> cpus 1-3, not cpu 0 9 | 10 | for x in `echo /sys/class/net/*/queues/*/{r,x}ps_cpus`; do echo e > $x; done 11 | 12 | # Change eth0 smp affinity list 13 | 14 | eth0_irqs=`cat /proc/interrupts | grep eth0 | awk '{print $1}' | tr -d :` 15 | for x in $eth0_irqs 16 | do 17 | echo 1-3 > /proc/irq/${x}/smp_affinity_list 18 | done 19 | 20 | #disable scaling 21 | for x in `echo /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor`; do echo performance > $x; done 22 | -------------------------------------------------------------------------------- /base/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -a 3 | . /configs/base/config.sh 4 | 5 | export LANIP=$(cat /configs/base/lanip || echo "192.168.2.1") 6 | 7 | # LANIF no longer owns LANIP. Use dummy IF to contain it 8 | ip link add name sprloop type dummy 9 | ip addr add $LANIP/32 dev sprloop 10 | ip link set dev sprloop up 11 | 12 | if [ "$LANIF" ]; then 13 | # set up static routes on LAN interface 14 | ip link set dev $LANIF up 15 | fi 16 | 17 | if [ "$NFT_OVERRIDE" ]; then 18 | . /configs/base/nft_rules.sh 19 | else 20 | # If configured as a leaf router, run those firwall rules instead 21 | if [ -f state/plugins/mesh/enabled ] && [ -f /plugins/plus/mesh_extension/mesh_rules.sh ]; then 22 | . /plugins/plus/mesh_extension/mesh_rules.sh 23 | else 24 | # Delete mesh bridge in case it exists 25 | ip link del br0 26 | . /scripts/nft_rules.sh 27 | fi 28 | fi 29 | 30 | ret=$? 31 | if [ $ret -ne 0 ]; then 32 | echo "Failed to load firewall rules, shutting down upstream interface" 33 | ip link set dev $WANIF down 34 | exit 1 35 | fi 36 | 37 | # traffic accounting 38 | . /scripts/accounting.sh 39 | 40 | # performance tuning 41 | /scripts/perftune.sh 42 | 43 | #mark initialization as finished 44 | flock /state/base/ready bash -c "sleep inf" 45 | -------------------------------------------------------------------------------- /base/setup-sdcard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # constrain journal size 4 | echo -e "[Journal]\n\nSystemMaxUse=50m\nSystemMaxFileSize=10M" > /etc/systemd/journald.conf 5 | # mount logs as tmpfs 6 | echo -e "tmpfs\t/tmp\ttmpfs\tdefaults,noatime,nosuid,size=100m\t0\t0\ntmpfs\t/var/tmp\ttmpfs\tdefaults,noatime,nosuid,size=512m\t0\t0\ntmpfs\t/var/log\ttmpfs\tdefaults,noatime,nosuid,mode=0755,size=100m\t0\t0\ntmpfs\t/run\ttmpfs\tdefaults,noatime,nosuid,mode=0755,size=10m\t0\t0\ntmpfs\t/var/run\ttmpfs\tdefaults,noatime,nosuid,mode=0755,size=10m\t0\t0\n" >> /etc/fstab 7 | 8 | bash base/setup.sh 9 | -------------------------------------------------------------------------------- /base/template_configs/.gitignore: -------------------------------------------------------------------------------- 1 | sae_passwords 2 | wpa2pskfile 3 | -------------------------------------------------------------------------------- /base/template_configs/auth/auth_tokens.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /base/template_configs/base/api.json: -------------------------------------------------------------------------------- 1 | { 2 | "InfluxDB": { 3 | "URL": "", 4 | "Org": "", 5 | "Bucket": "", 6 | "Token": "" 7 | }, 8 | "Plugins": [ 9 | { 10 | "Name": "dns-block-extension", 11 | "URI": "dns/block", 12 | "UnixPath": "/state/dns/dns_block_plugin", 13 | "Enabled": true 14 | }, 15 | { 16 | "Name": "dns-log-extension", 17 | "URI": "dns/log", 18 | "UnixPath": "/state/dns/dns_log_plugin", 19 | "Enabled": true 20 | }, 21 | { 22 | "Name": "plugin-lookup", 23 | "URI": "lookup", 24 | "UnixPath": "/state/plugins/plugin-lookup/lookup_plugin", 25 | "Enabled": true 26 | }, 27 | { 28 | "Name": "wireguard", 29 | "URI": "wireguard", 30 | "UnixPath": "/state/plugins/wireguard/wireguard_plugin", 31 | "Enabled": true 32 | }, 33 | { 34 | "Name": "dyndns", 35 | "URI": "dyndns", 36 | "UnixPath": "/state/plugins/dyndns/dyndns_plugin", 37 | "Enabled": false, 38 | "ComposeFilePath": "dyndns/docker-compose.yml" 39 | }, 40 | { 41 | "Name": "db", 42 | "URI": "db", 43 | "UnixPath": "/state/plugins/db/socket", 44 | "Enabled": true 45 | }, 46 | { 47 | "Name": "PPP", 48 | "Enabled": false, 49 | "ComposeFilePath": "ppp/docker-compose.yml" 50 | }, 51 | { 52 | "Name": "WIFIUPLINK", 53 | "Enabled": false, 54 | "ComposeFilePath": "wifi_uplink/docker-compose.yml" 55 | } 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /base/template_configs/base/config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # comment below to DISABLE ssh, API from the Upstream Interface 4 | UPSTREAM_SERVICES_ENABLE=1 5 | 6 | # Uncomment below to use SPR without wifi, 7 | # as a VPN gateway for example 8 | #VIRTUAL_SPR=1 9 | 10 | WANIF=eth0 11 | RUN_WAN_DHCP=true 12 | RUN_WAN_DHCP_IPV=4 13 | # Uncomment the next line if a second ethernet port goes to wired LAN 14 | #LANIF=eth1 15 | 16 | DOCKERNET=172.17.0.0/16 17 | DOCKERIF=docker0 18 | 19 | WIREGUARD_PORT=51280 20 | -------------------------------------------------------------------------------- /base/template_configs/base/dhcp.json: -------------------------------------------------------------------------------- 1 | {"TinyNets":["192.168.2.0/24"],"LeaseTime":"24h0m0s"} 2 | -------------------------------------------------------------------------------- /base/template_configs/base/firewall.json: -------------------------------------------------------------------------------- 1 | { 2 | "ForwardingRules": [], 3 | "BlockRules": [], 4 | "ForwardingBlockRules": [], 5 | "ServicePorts": [ 6 | { 7 | "Protocol": "tcp", 8 | "Port": "22", 9 | "UpstreamEnabled": true 10 | }, 11 | { 12 | "Protocol": "udp", 13 | "Port": "68", 14 | "UpstreamEnabled": true 15 | }, 16 | { 17 | "Protocol": "tcp", 18 | "Port": "80", 19 | "UpstreamEnabled": true 20 | }, 21 | { 22 | "Protocol": "tcp", 23 | "Port": "443", 24 | "UpstreamEnabled": true 25 | } 26 | ], 27 | "Endpoints": [], 28 | "MulticastPorts": [ 29 | { 30 | "Port": "5353", 31 | "Upstream": false 32 | }, 33 | { 34 | "Port": "1900", 35 | "Upstream": false 36 | } 37 | ], 38 | "PingLan": true, 39 | "PingWan": false 40 | } 41 | -------------------------------------------------------------------------------- /base/template_configs/base/interfaces.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /base/template_configs/base/lanip: -------------------------------------------------------------------------------- 1 | 192.168.2.1 2 | -------------------------------------------------------------------------------- /base/template_configs/base/virtual-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This variant of the configuration is for running 3 | # without a wifi AP. See https://www.supernetworks.org/pages/docs/virtual_spr 4 | 5 | # Run in VIRTUAL mode (bridge network rather than host mode) 6 | VIRTUAL_SPR=1 7 | 8 | # uncomment below to allow API connections from the internet, and modify docker-compose.yml to no longer bind to 127.0.0.1 9 | # WARNING: the API will likely be vulnerable to MITM attacks 10 | #VIRTUAL_SPR_API_INTERNET=1 11 | 12 | #comment below to DISABLE ssh, API from the docker network altogether 13 | UPSTREAM_SERVICES_ENABLE=1 14 | WANIF=eth0 15 | RUN_WAN_DHCP=true 16 | RUN_WAN_DHCP_IPV=4 17 | 18 | WIREGUARD_PORT=51280 19 | -------------------------------------------------------------------------------- /base/template_configs/devices/devices.json: -------------------------------------------------------------------------------- 1 | { 2 | "11:22:33:44:55:66": { 3 | "Name": "Example Device Entry", 4 | "MAC": "11:22:33:44:55:66", 5 | "WGPubKey": "", 6 | "VLANMatch": "", 7 | "RecentIP": "", 8 | "PSKEntry": { 9 | "Type": "", 10 | "Psk": "" 11 | }, 12 | "Groups": [ 13 | ], 14 | "Policies": [ 15 | "dns", 16 | "wan" 17 | ], 18 | "DeviceTags": [] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /base/template_configs/devices/groups.json: -------------------------------------------------------------------------------- 1 | [ 2 | 3 | ] 4 | -------------------------------------------------------------------------------- /base/template_configs/dhcp/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/base/template_configs/dhcp/.gitignore -------------------------------------------------------------------------------- /base/template_configs/dns/Corefile: -------------------------------------------------------------------------------- 1 | . { 2 | block enable_superapi 3 | 4 | hosts /state/dns/local_mappings { 5 | ttl 60 6 | reload 30s 7 | fallthrough 8 | } 9 | 10 | forward . tls://1.1.1.1 tls://1.0.0.1 { 11 | tls_servername cloudflare-dns.com 12 | max_concurrent 1000 13 | } 14 | 15 | forward . tls://1.1.1.3 tls://1.0.0.3 { 16 | spr_policy dns:family 17 | tls_servername cloudflare-dns.com 18 | max_concurrent 1000 19 | } 20 | 21 | jsonlog { 22 | enable_superapi 23 | } 24 | log 25 | errors { 26 | stacktrace 27 | } 28 | cache 30 29 | } 30 | -------------------------------------------------------------------------------- /base/template_configs/ppp/chap-secrets: -------------------------------------------------------------------------------- 1 | # Secrets for authentication using CHAP 2 | # client server secret IP addresses 3 | 4 | 5 | "user@provider" * "password" 6 | -------------------------------------------------------------------------------- /base/template_configs/ppp/peers/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/base/template_configs/ppp/peers/.gitignore -------------------------------------------------------------------------------- /base/template_configs/ppp/peers/peer_sample: -------------------------------------------------------------------------------- 1 | # Minimalistic default options file for DSL/PPPoE connections 2 | 3 | noipdefault 4 | defaultroute 5 | replacedefaultroute 6 | #hide-password 7 | #lcp-echo-interval 30 8 | #lcp-echo-failure 4 9 | noauth 10 | persist 11 | #mtu 1492 12 | #persist 13 | #maxfail 0 14 | #holdoff 20 15 | # Note, make sure to update this for the appropriate interface. 16 | # The below example assumes a tagged VLAN 17 | plugin rp-pppoe.so eth0.201 18 | user "user@provider" 19 | -------------------------------------------------------------------------------- /base/template_configs/ppp/vars.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PPPIF=eth0 3 | PPP_VLANID=201 4 | PPP_PROVIDER=provider-config 5 | 6 | -------------------------------------------------------------------------------- /base/template_configs/scripts/gen_coredhcp_yaml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | . configs/base/config.sh 3 | 4 | cat << END 5 | # Note, this is generated by gen_coredhcp_yaml.sh. Modify that to make changes 6 | server4: 7 | # Listens on all interfaces when none are configured. Note that iptables should block dhcp from $WANIF 8 | plugins: 9 | - tiny_subnets: 10 | 11 | END 12 | -------------------------------------------------------------------------------- /base/template_configs/scripts/gen_watchdog.sh: -------------------------------------------------------------------------------- 1 | . configs/base/config.sh 2 | 3 | cat << EOF 4 | watchdog-device = /dev/watchdog 5 | watchdog-timeout = 300 6 | #interface = $VLANIF 7 | 8 | EOF 9 | -------------------------------------------------------------------------------- /base/template_configs/watchdog/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/base/template_configs/watchdog/.gitignore -------------------------------------------------------------------------------- /base/template_configs/wifi/sae_passwords: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/base/template_configs/wifi/sae_passwords -------------------------------------------------------------------------------- /base/template_configs/wifi/wpa2pskfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/base/template_configs/wifi/wpa2pskfile -------------------------------------------------------------------------------- /base/template_configs/wireguard/wg0.conf: -------------------------------------------------------------------------------- 1 | [Interface] 2 | PrivateKey = privkey 3 | ListenPort = 51280 4 | 5 | #[Peer] 6 | #PublicKey = pubkey 7 | #PresharedKey = sharedkey 8 | #AllowedIPs = 192.168.3.3/32 9 | -------------------------------------------------------------------------------- /db/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS builder 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends nftables iproute2 netcat-traditional inetutils-ping net-tools nano ca-certificates git curl wget 5 | RUN mkdir /code 6 | WORKDIR /code 7 | ARG TARGETARCH 8 | RUN wget https://dl.google.com/go/go1.24.2.linux-${TARGETARCH}.tar.gz 9 | RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.2.linux-${TARGETARCH}.tar.gz 10 | ENV PATH="/usr/local/go/bin:$PATH" 11 | COPY code/ /code/ 12 | 13 | ARG USE_TMPFS=true 14 | RUN --mount=type=tmpfs,target=/tmpfs \ 15 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 16 | go build -o /boltapi cmd/boltapi/main.go 17 | 18 | FROM ghcr.io/spr-networks/container_template:latest 19 | ENV DEBIAN_FRONTEND=noninteractive 20 | RUN apt-get update && rm -rf /var/lib/apt/lists/* 21 | COPY scripts /scripts/ 22 | COPY --from=builder /boltapi / 23 | ENTRYPOINT ["/scripts/startup.sh"] 24 | -------------------------------------------------------------------------------- /db/code/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | go build -o boltapi cmd/boltapi/main.go 3 | -------------------------------------------------------------------------------- /db/code/go.mod: -------------------------------------------------------------------------------- 1 | module boltapi 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.2 6 | 7 | require ( 8 | github.com/PaesslerAG/gval v1.2.4 9 | github.com/PaesslerAG/jsonpath v0.1.1 10 | github.com/gorilla/mux v1.8.1 11 | github.com/hashicorp/go-msgpack v0.5.5 12 | github.com/spr-networks/sprbus v0.1.7 13 | go.etcd.io/bbolt v1.4.0 14 | ) 15 | 16 | require ( 17 | github.com/golang/protobuf v1.5.3 // indirect 18 | github.com/moby/moby v23.0.3+incompatible // indirect 19 | github.com/moby/pubsub v1.0.0 // indirect 20 | github.com/shopspring/decimal v1.4.0 // indirect 21 | github.com/sirupsen/logrus v1.9.0 // indirect 22 | golang.org/x/net v0.17.0 // indirect 23 | golang.org/x/sys v0.32.0 // indirect 24 | golang.org/x/text v0.13.0 // indirect 25 | google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect 26 | google.golang.org/grpc v1.54.0 // indirect 27 | google.golang.org/protobuf v1.30.0 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /db/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -a 3 | . /configs/base/config.sh 4 | 5 | STATE_DIR="/state/plugins/db" 6 | 7 | if [ ! -d "$STATE_DIR" ]; then 8 | mkdir "$STATE_DIR" 9 | fi 10 | 11 | /boltapi 12 | -------------------------------------------------------------------------------- /dhcp/.gitignore: -------------------------------------------------------------------------------- 1 | leases.txt 2 | config/zones/* 3 | -------------------------------------------------------------------------------- /dhcp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS builder 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends nftables iproute2 netcat-traditional inetutils-ping net-tools nano ca-certificates git curl wget 5 | RUN apt-get install -y --no-install-recommends clang 6 | RUN mkdir /code 7 | WORKDIR /code 8 | 9 | ARG TARGETARCH 10 | RUN wget https://dl.google.com/go/go1.24.2.linux-$TARGETARCH.tar.gz 11 | RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.2.linux-$TARGETARCH.tar.gz 12 | ENV PATH="/usr/local/go/bin:$PATH" 13 | 14 | # Build CoreDHCP 15 | ENV CC=clang 16 | ARG CACHEBUST=1 17 | RUN git clone https://github.com/spr-networks/coredhcp 18 | WORKDIR /code/coredhcp 19 | ARG USE_TMPFS=true 20 | RUN --mount=type=tmpfs,target=/tmpfs \ 21 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 22 | go build -o /coredhcpd ./cmds/coredhcp && \ 23 | go build -o /coredhcp_client ./cmds/exdhcp/dhclient/ 24 | 25 | 26 | FROM ghcr.io/spr-networks/container_template:latest 27 | ENV DEBIAN_FRONTEND=noninteractive 28 | RUN apt-get update && apt-get install -y --no-install-recommends jq ethtool isc-dhcp-client inotify-tools psmisc && rm -rf /var/lib/apt/lists/* 29 | COPY --from=builder /coredhcpd / 30 | COPY --from=builder /coredhcp_client / 31 | COPY scripts /scripts 32 | COPY scripts/dhclient-gateway-hook.sh /etc/dhcp/dhclient-enter-hooks.d/spr-gateway 33 | 34 | ENTRYPOINT ["/scripts/startup.sh"] 35 | -------------------------------------------------------------------------------- /dhcp/scripts/dhclient-gateway-hook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case "$reason" in 4 | BOUND|RENEW) 5 | # Commands to run when a lease is obtained or renewed 6 | GATEWAY=$(echo ${new_routers} | awk '{print $1}' | cut -d ',' -f1 | cut -d ';' -f1) 7 | NAME=$interface 8 | echo $GATEWAY > /state/dhcp-client/gateway.${NAME} 9 | ;; 10 | EXPIRE|FAIL) 11 | # Commands to run when a lease expires or fails 12 | ;; 13 | *) 14 | # Any other reason 15 | ;; 16 | esac 17 | -------------------------------------------------------------------------------- /dhcp/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Do not run DHCPD in mesh mode 3 | if [ ! -f state/plugins/mesh/enabled ]; then 4 | /coredhcpd -c /configs/dhcp/coredhcp.yml 5 | fi 6 | -------------------------------------------------------------------------------- /dns/.gitignore: -------------------------------------------------------------------------------- 1 | local_mappings 2 | configs/Corefile 3 | -------------------------------------------------------------------------------- /dns/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Do not run DNS in mesh mode 3 | if [ ! -f state/plugins/mesh/enabled ]; then 4 | /coredns -conf /configs/dns/Corefile 5 | fi 6 | -------------------------------------------------------------------------------- /dyndns/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS builder 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends nftables iproute2 netcat-traditional inetutils-ping net-tools nano ca-certificates git curl wget 5 | RUN mkdir /code/ 6 | WORKDIR /code/ 7 | ARG TARGETARCH 8 | RUN wget https://dl.google.com/go/go1.24.2.linux-${TARGETARCH}.tar.gz 9 | RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.2.linux-${TARGETARCH}.tar.gz 10 | ENV PATH="/usr/local/go/bin:$PATH" 11 | RUN git clone --branch v3.0.7 https://github.com/TimothyYe/godns.git 12 | WORKDIR godns/cmd/godns 13 | ARG USE_TMPFS=true 14 | RUN --mount=type=tmpfs,target=/tmpfs \ 15 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 16 | go mod download && go build 17 | WORKDIR /code/ 18 | COPY code/* . 19 | RUN --mount=type=tmpfs,target=/tmpfs go build 20 | 21 | FROM ghcr.io/spr-networks/container_template:latest 22 | ENV DEBIAN_FRONTEND=noninteractive 23 | COPY --from=builder /code/godns/cmd/godns/godns / 24 | COPY --from=builder /code/dyndns_plugin / 25 | ENTRYPOINT ["/dyndns_plugin"] 26 | -------------------------------------------------------------------------------- /dyndns/code/go.mod: -------------------------------------------------------------------------------- 1 | module dyndns_plugin 2 | 3 | go 1.17 4 | 5 | require github.com/gorilla/mux v1.8.1 6 | -------------------------------------------------------------------------------- /dyndns/code/go.sum: -------------------------------------------------------------------------------- 1 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 2 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 3 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= 4 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 5 | -------------------------------------------------------------------------------- /dyndns/docker-compose.yml: -------------------------------------------------------------------------------- 1 | x-logging: 2 | &default-logging 3 | driver: journald 4 | 5 | x-labels: 6 | &default-labels 7 | org.supernetworks.ci: ${CI:-false} 8 | org.supernetworks.version: ${RELEASE_VERSION:-latest}${RELEASE_CHANNEL:-} 9 | 10 | services: 11 | dyndns: 12 | container_name: superdyndns 13 | image: ghcr.io/spr-networks/super_dyndns:${RELEASE_VERSION:-latest}${RELEASE_CHANNEL:-} 14 | build: 15 | context: . 16 | labels: *default-labels 17 | x-bake: 18 | tags: 19 | - ghcr.io/spr-networks/super_dyndns:latest${RELEASE_CHANNEL:-} 20 | - ghcr.io/spr-networks/super_dyndns:${RELEASE_VERSION:-latest}${RELEASE_CHANNEL:-} 21 | network_mode: bridge 22 | logging: *default-logging 23 | volumes: 24 | - "${SUPERDIR}/state/plugins/dyndns/:/state/plugins/dyndns/" 25 | - "${SUPERDIR}/configs/dyndns/:/configs/dyndns/" 26 | -------------------------------------------------------------------------------- /flowgather/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM Ubuntu:24.04 as builder 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update 4 | RUN apt-get install -y --no-install-recommends nftables iproute2 netcat-traditional inetutils-ping net-tools nano ca-certificates git curl wget 5 | RUN apt-get install -y --no-install-recommends clang 6 | RUN apt-get install -y --no-install-recommends libpcap-dev 7 | RUN mkdir /code 8 | WORKDIR /code 9 | ARG TARGETARCH 10 | RUN wget https://dl.google.com/go/go1.24.2.linux-${TARGETARCH}.tar.gz 11 | RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.24.2.linux-${TARGETARCH}.tar.gz 12 | ENV PATH="/usr/local/go/bin:$PATH" 13 | ENV CC=clang 14 | COPY code/ /code/ 15 | ARG USE_TMPFS=true 16 | RUN --mount=type=tmpfs,target=/tmpfs \ 17 | [ "$USE_TMPFS" = "true" ] && ln -s /tmpfs /root/go; \ 18 | go mod tidy && go build -ldflags="-s -w" -o /flowgather 19 | COPY scripts /scripts/ 20 | 21 | FROM ghcr.io/spr-networks/container_template:latest 22 | ENV DEBIAN_FRONTEND=noninteractive 23 | RUN mkdir /data 24 | COPY --from=/flowgather / 25 | ENTRYPOINT ["/scripts/startup.sh"] 26 | -------------------------------------------------------------------------------- /flowgather/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export DOCKER_BUILDKIT=1 3 | docker build . -t flowgather 4 | 5 | -------------------------------------------------------------------------------- /flowgather/code/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/spr-networks/flowgather 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/google/gopacket v1.1.19 7 | github.com/vishvananda/netlink v1.1.0 8 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /flowgather/scripts/startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GOGC=4000 /flowgather 3 | -------------------------------------------------------------------------------- /frontend/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /output 3 | /build 4 | /node_modules 5 | ios/Pods 6 | ios/build 7 | ios/spr.xcworkspace/xcuserdata 8 | macos/Pods 9 | macos/build 10 | macos/spr.xcworkspace/xcuserdata 11 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "semi": false 5 | } 6 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=$BUILDPLATFORM node:18 as build 2 | 3 | WORKDIR /app 4 | COPY . ./ 5 | ARG USE_TMPFS=true 6 | RUN --mount=type=tmpfs,target=/tmpfs \ 7 | [ "$USE_TMPFS" = "true" ] && \ 8 | mkdir /tmpfs/cache /tmpfs/node_modules && \ 9 | ln -s /tmpfs/node_modules /app/node_modules && \ 10 | ln -s /tmpfs/cache /usr/local/share/.cache; \ 11 | yarn install --network-timeout 86400000 && yarn run build 12 | 13 | FROM alpine 14 | COPY --from=build /app/build/ /app/build/ 15 | -------------------------------------------------------------------------------- /frontend/Dockerfile-helper: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | COPY ./output/build /build/ 3 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # SPR UI 2 | 3 | The UI is built with [React Native](https://reactnative.dev/) and [Native Base](https://docs.nativebase.io/). 4 | 5 | **Build status** 6 | 7 | dev: ![action workflow](https://github.com/spr-networks/super/actions/workflows/test-ui.yml/badge.svg?branch=dev) 8 | 9 | main: ![action workflow](https://github.com/spr-networks/super/actions/workflows/test-ui.yml/badge.svg?branch=dev) 10 | 11 | ## Running 12 | 13 | To test locally: 14 | ```bash 15 | yarn dev 16 | ``` 17 | 18 | You can point it to your SPR instance with the `REACT_APP_API` variable: 19 | ```bash 20 | REACT_APP_API=http://192.168.2.1 yarn start 21 | ``` 22 | 23 | If you want to use the MockAPI (with only a frontend and no SPR system running): 24 | ```bash 25 | REACT_APP_API=mock yarn start 26 | ``` 27 | 28 | iOS version: 29 | ```bash 30 | yarn ios 31 | # might have to specify iPhone version: 32 | npx react-native run-ios --simulator="iPhone 14" 33 | ``` 34 | 35 | ## iOS build 36 | 37 | install deps: 38 | ```sh 39 | yarn setup:ios 40 | ``` 41 | 42 | open ios/spr.xcworkspace in Xcode && build/run 43 | 44 | for testflight we need: 45 | 46 | + production certificate 47 | + App Store distribution provisioning profile 48 | 49 | = same as app store dist 50 | -------------------------------------------------------------------------------- /frontend/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spr", 3 | "displayName": "Supernetworks Secure Programmable Router" 4 | } 5 | -------------------------------------------------------------------------------- /frontend/babel-plugin-macros.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'fontawesome-svg-core': { 3 | license: 'free' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: ['macros'] 4 | } 5 | -------------------------------------------------------------------------------- /frontend/index.js: -------------------------------------------------------------------------------- 1 | // native app entrypoint 2 | import { AppRegistry, LogBox } from 'react-native' 3 | import App from './src/App' 4 | import { name as appName } from './app.json' 5 | 6 | // should be fixed in next native-base + use React 18 7 | LogBox.ignoreLogs(['When server rendering']) 8 | 9 | AppRegistry.registerComponent(appName, () => App) 10 | -------------------------------------------------------------------------------- /frontend/ios/spr.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/ios/spr.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/ios/spr.xcodeproj/project.xcworkspace/xcuserdata/po.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr.xcodeproj/project.xcworkspace/xcuserdata/po.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /frontend/ios/spr.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/ios/spr.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/ios/spr/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface AppDelegate : UIResponder 6 | 7 | @property (nonatomic, strong) UIWindow *window; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo-8.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "logo-7.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "logo-6.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "logo-5.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "logo-4.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "logo-3.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "logo-2.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "logo-1.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "logo.png", 53 | "idiom" : "ios-marketing", 54 | "scale" : "1x", 55 | "size" : "1024x1024" 56 | } 57 | ], 58 | "info" : { 59 | "author" : "xcode", 60 | "version" : 1 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-1.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-2.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-3.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-4.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-5.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-6.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-7.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo-8.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/ios/spr/Images.xcassets/AppIcon.appiconset/logo.png -------------------------------------------------------------------------------- /frontend/ios/spr/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /frontend/ios/spr/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/ios/spr/sprRelease.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.developer.networking.wifi-info 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/ios/sprTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /frontend/ios/update-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # update ios project file to use same version as ui and current date as build number 3 | 4 | DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) 5 | VERSION=$(git describe --tags --abbrev=0 | grep -Eo '[0-9]+\.[0-9]+.[0-9]+') 6 | 7 | echo "+ git version tag = $VERSION" 8 | 9 | if [ -z "$VERSION" ]; then 10 | VERSION=$(cat $DIR/../package.json | grep '"version"' | cut -d '"' -f4 | grep -Eo '[0-9]+\.[0-9]+.[0-9]+') 11 | fi 12 | 13 | BUILD=$(date +"%Y%m%d%H%M%S") 14 | 15 | echo "+ iOS version = $VERSION" 16 | echo "+ build = $BUILD" 17 | 18 | PROJECT_DIR="$DIR/spr.xcodeproj" 19 | PROJECT_FILE="$PROJECT_DIR/project.pbxproj" 20 | 21 | cat "$PROJECT_FILE" | \ 22 | sed "s/MARKETING_VERSION = [^;]*;/MARKETING_VERSION = $VERSION;/g" | \ 23 | sed "s/CURRENT_PROJECT_VERSION = [^;]*;/CURRENT_PROJECT_VERSION = $BUILD;/g" \ 24 | > "${PROJECT_FILE}.new" && \ 25 | mv "${PROJECT_FILE}.new" $PROJECT_FILE 26 | -------------------------------------------------------------------------------- /frontend/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src", 4 | "paths": { 5 | "*": ["src/*"], 6 | "assets": ["src/assets"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # CocoaPods 2 | Pods/ 3 | -------------------------------------------------------------------------------- /frontend/macos/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native-macos/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | target 'supernetworks-SPR-ui-macOS' do 5 | platform :macos, '10.15' 6 | use_native_modules! 7 | use_react_native!( 8 | :path => '../node_modules/react-native-macos', 9 | 10 | # To use Hermes, install the `hermes-engine-darwin` npm package, e.g.: 11 | # $ yarn add 'hermes-engine-darwin@~0.5.3' 12 | # 13 | # Then enable this option: 14 | # :hermes_enabled => true 15 | ) 16 | 17 | # Pods specifically for macOS target 18 | end 19 | 20 | target 'supernetworks-SPR-ui-iOS' do 21 | platform :ios, '11' 22 | use_native_modules! 23 | use_react_native!(:path => '../node_modules/react-native-macos') 24 | 25 | # Enables Flipper. 26 | # 27 | # Note that if you have use_frameworks! enabled, Flipper will not work and 28 | # you should disable these next few lines. 29 | use_flipper! 30 | 31 | post_install do |installer| 32 | flipper_post_install(installer) 33 | react_native_post_install(installer) 34 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 35 | end 36 | # Pods specifically for iOS target 37 | end 38 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-iOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-iOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | @implementation AppDelegate 8 | 9 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 10 | { 11 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 12 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 13 | moduleName:@"supernetworks-SPR-ui" 14 | initialProperties:nil]; 15 | 16 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 17 | 18 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 19 | UIViewController *rootViewController = [UIViewController new]; 20 | rootViewController.view = rootView; 21 | self.window.rootViewController = rootViewController; 22 | [self.window makeKeyAndVisible]; 23 | return YES; 24 | } 25 | 26 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 27 | { 28 | #if DEBUG 29 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 30 | #else 31 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 32 | #endif 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-iOS/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-iOS/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-iOS/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class RCTBridge; 4 | 5 | @interface AppDelegate : NSObject 6 | 7 | @property (nonatomic, readonly) RCTBridge *bridge; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | 6 | @interface AppDelegate () 7 | 8 | @end 9 | 10 | @implementation AppDelegate 11 | 12 | - (void)awakeFromNib { 13 | [super awakeFromNib]; 14 | 15 | _bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:nil]; 16 | } 17 | 18 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 19 | // Insert code here to initialize your application 20 | } 21 | 22 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 23 | // Insert code here to tear down your application 24 | } 25 | 26 | #pragma mark - RCTBridgeDelegate Methods 27 | 28 | - (NSURL *)sourceURLForBridge:(__unused RCTBridge *)bridge { 29 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; // .jsbundle; 30 | //return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:@"main"]; // .jsbundle; 31 | 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/ViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ViewController : NSViewController 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/ViewController.m: -------------------------------------------------------------------------------- 1 | #import "ViewController.h" 2 | #import "AppDelegate.h" 3 | 4 | #import 5 | 6 | @implementation ViewController 7 | 8 | - (void)viewDidLoad { 9 | [super viewDidLoad]; 10 | 11 | RCTBridge *bridge = [((AppDelegate *)[NSApp delegate])bridge]; 12 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"spr" initialProperties:nil]; 13 | 14 | NSView *view = [self view]; 15 | 16 | [view addSubview:rootView]; 17 | [rootView setBackgroundColor:[NSColor windowBackgroundColor]]; 18 | [rootView setFrame:[view bounds]]; 19 | [rootView setAutoresizingMask:(NSViewMinXMargin | NSViewMinXMargin | NSViewMinYMargin | NSViewMaxYMargin | NSViewWidthSizable | NSViewHeightSizable)]; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | int main(int argc, const char *argv[]) { 4 | return NSApplicationMain(argc, argv); 5 | } 6 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui-macOS/supernetworks-SPR-ui.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /frontend/macos/supernetworks-SPR-ui.xcworkspace/xcuserdata/po.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/macos/supernetworks-SPR-ui.xcworkspace/xcuserdata/po.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /frontend/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | const path = require('path'); 9 | /*const extraNodeModules = { 10 | 'src': path.resolve(__dirname + '/src'), 11 | };*/ 12 | const watchFolders = [ 13 | path.resolve(__dirname + '/src') 14 | ] 15 | 16 | const projectRoot = __dirname + '/src' 17 | 18 | module.exports = { 19 | //projectRoot, 20 | transformer: { 21 | getTransformOptions: async () => ({ 22 | transform: { 23 | experimentalImportSupport: false, 24 | inlineRequires: true, 25 | }, 26 | }), 27 | }, 28 | resolver: { 29 | //extraNodeModules 30 | nodeModulesPaths: [path.join(__dirname, 'src'), path.join(__dirname, '../node_modules')], 31 | /* 32 | resolve: { 33 | modules: [path.join(__dirname, 'src'), 'node_modules'], 34 | alias: { 35 | react: path.join(__dirname, 'node_modules', 'react') 36 | } 37 | 38 | */ 39 | }, 40 | //watchFolders, 41 | }; 42 | -------------------------------------------------------------------------------- /frontend/public/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/public/apple-icon.png -------------------------------------------------------------------------------- /frontend/public/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/public/bg.jpg -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/public/fonts/ATTRIBUTION: -------------------------------------------------------------------------------- 1 | "Font Awesome by Dave Gandy - http://fontawesome.io". 2 | -------------------------------------------------------------------------------- /frontend/public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /frontend/public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 17 | 22 | 26 | 27 | Supernetworks SPR 28 | 29 | 30 | 31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "SPR", 3 | "name": "Supernetworks SPR", 4 | "icons": [ 5 | { 6 | "src": "favicon.png", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "apple-icon.png", 12 | "sizes": "64x64 32x32 24x24 16x16", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "./index.html", 17 | "display": "standalone", 18 | "theme_color": "#000000", 19 | "background_color": "#ffffff" 20 | } 21 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | 2 | /* mini icons in sidebar */ 3 | 4 | /* submenu icons */ 5 | li ul li .sidebar-mini-icon > .fa { 6 | margin-left: 5px; 7 | } 8 | 9 | /* TODO smtg like this 10 | .sidebar-mini .sidebar-normal { text-align: center } 11 | */ 12 | 13 | .card-stats { 14 | width: 100%; 15 | } 16 | 17 | .card-stats .icon-big { 18 | font-size: 5.0em; 19 | margin-top: -20px; 20 | min-height: 64px; 21 | } 22 | 23 | .react-tagsinput .react-tagsinput-tag { 24 | transition: all 300ms ease 0s; 25 | transition: none !important; 26 | border-color: #66615B; 27 | background-color: #66615B; 28 | } 29 | 30 | .nc-wireguard { 31 | background-image: url(assets/img/icon-wg.svg); 32 | background-position: center center; 33 | background-repeat: no-repeat; 34 | opacity: 0.8; 35 | } 36 | .nc-wireguard:before { 37 | content: '\ffff'; 38 | font-size: 1.2rem; 39 | } 40 | 41 | /* only display icon in mini-mode -- somewhat buggy */ 42 | /* 43 | .sidebar-mini .sidebar .sidebar-normal { 44 | line-height: 0px; 45 | color: transparent; 46 | } 47 | 48 | .sidebar-mini .sidebar:hover .sidebar-normal { 49 | line-height: 30px; 50 | color: inherit; 51 | } 52 | */ 53 | 54 | .modal label, 55 | .modal .form-group label { 56 | font-size: .8571em; 57 | margin-bottom: 5px; 58 | color: #9a9a9a; 59 | } 60 | 61 | /* badge fix */ 62 | .badge.badge-secondary { 63 | color: #fff; 64 | background-color: #6c757d; 65 | } 66 | 67 | @media (min-width: 576px) { 68 | /* only align text right if large enough device */ 69 | .sm-text-right { 70 | text-align: right; 71 | } 72 | } -------------------------------------------------------------------------------- /frontend/src/AppContext.js: -------------------------------------------------------------------------------- 1 | import React, { createContext } from 'react' 2 | 3 | export const AppContext = createContext({ 4 | activeSidebarItem: 'home', 5 | setActiveSidebarItem: (sidebarItem) => {}, 6 | isNavbarOpen: false, 7 | setIsNavbarOpen: (isNavbarOpen) => {}, 8 | isSimpleMode: false, 9 | setIsSimpleMode: (isSimpleMode) => {}, 10 | isWifiDisabled: false, 11 | isPlusDisabled: true, 12 | isMeshNode: false, 13 | isFeaturesInitialized: false, 14 | features: [], 15 | devices: [], 16 | getDevices: () => {}, 17 | getDevice: () => {}, 18 | getGroups: () => [], 19 | viewSettings: {}, 20 | setViewSettings: () => {} 21 | }) 22 | 23 | export const alertState = { 24 | alert: () => {} 25 | } 26 | 27 | export const modalState = { 28 | modal: () => {}, 29 | setShowModal: () => {}, 30 | toggleModal: () => {} 31 | } 32 | 33 | // TODO Toast 34 | export const AlertContext = createContext(alertState) 35 | export const ModalContext = createContext(modalState) 36 | -------------------------------------------------------------------------------- /frontend/src/DeviceInfo.js: -------------------------------------------------------------------------------- 1 | export default { 2 | initDevice: () => { 3 | console.log('init web device') 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/Notifications.js: -------------------------------------------------------------------------------- 1 | export const init = (...props) => { 2 | //Notification.requestPermission() ... 3 | } 4 | 5 | export function notification(title, body) { 6 | let msg = `${title}, ${body}` 7 | if (!('Notification' in window)) { 8 | return 9 | } 10 | 11 | if (Notification.permission === 'denied') { 12 | return 13 | } 14 | 15 | if (Notification.permission === 'granted') { 16 | var notification = new Notification(msg) 17 | } else if (Notification.permission !== 'denied') { 18 | Notification.requestPermission().then(function (permission) { 19 | if (permission === 'granted') { 20 | var notification = new Notification(msg) 21 | } 22 | }) 23 | } 24 | 25 | return 26 | } 27 | 28 | export default { 29 | init, 30 | notification 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/__tests__/App.js: -------------------------------------------------------------------------------- 1 | import 'react-native' 2 | import React from 'react' 3 | import App from '../App.web' 4 | 5 | // Note: test renderer must be required after react-native. 6 | import TestRenderer from 'react-test-renderer' 7 | 8 | it('renders correctly', () => { 9 | //TestRenderer.create() 10 | expect(1).toBeTruthy() 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/src/__tests__/Pfw.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { act, render, screen, waitFor } from 'test-utils' 3 | 4 | import { pfwAPI, saveLogin } from 'api' 5 | import PFW from 'views/Firewall/Pfw' 6 | //import FlowList from 'components/Flow/FlowList' 7 | import createServer from 'api/MockAPI' 8 | 9 | let server 10 | 11 | beforeAll(() => { 12 | server = createServer() 13 | saveLogin('admin', 'admin') 14 | }) 15 | 16 | afterAll(() => { 17 | server.shutdown() 18 | }) 19 | 20 | describe('PFW', () => { 21 | test('Flow list', async () => { 22 | const utils = render() 23 | 24 | // wait for data to be populated 25 | await waitFor(() => expect(screen.getByDisplayValue('213.24.76.23')).toBeTruthy()) 26 | }) 27 | 28 | test('api', async () => { 29 | let config = await pfwAPI.config() 30 | expect(config.BlockRules.length).toBeGreaterThan(0) 31 | }) 32 | 33 | test('Add flow', async () => { 34 | const utils = render() 35 | expect(screen.getByDisplayValue('NewFlow')).toBeTruthy() 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /frontend/src/__tests__/Widgets.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, screen } from 'test-utils' 3 | 4 | import { WifiClients } from 'components/Dashboard/WifiWidgets' 5 | 6 | describe('Wifi Widgets', () => { 7 | test('test num wifi clients', async () => { 8 | const utils = render() 9 | expect(screen.getByText('Active WiFi Clients')).toBeTruthy() 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /frontend/src/api/Alerts.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIAlerts extends API { 4 | constructor() { 5 | super('') 6 | } 7 | 8 | list() { 9 | return this.get('/alerts') 10 | } 11 | 12 | add(data) { 13 | return this.put('/alerts', data) 14 | } 15 | 16 | update(index, data) { 17 | return this.put(`/alerts/${index}`, data) 18 | } 19 | 20 | remove(index) { 21 | return this.delete(`/alerts/${index}`, {}) 22 | } 23 | } 24 | 25 | export const alertsAPI = new APIAlerts() 26 | -------------------------------------------------------------------------------- /frontend/src/api/Auth.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIAuth extends API { 4 | constructor() { 5 | super('/') 6 | } 7 | 8 | tokens() { 9 | return this.get('tokens'); 10 | } 11 | 12 | putToken(Name, Expire, ScopedPaths) { 13 | return this.put('tokens', { Expire, Name, ScopedPaths }); 14 | } 15 | 16 | deleteToken(Token) { 17 | return this.delete('tokens', { Token }); 18 | } 19 | 20 | registerOTP(Otp) { 21 | return this.put('otp_register', {Name: 'admin', Code: Otp}) 22 | } 23 | 24 | validateOTP(Otp, UpdateAlwaysOn=false, AlwaysOn=false) { 25 | let v = {Name: 'admin', Code: Otp} 26 | if (UpdateAlwaysOn) { 27 | v["UpdateAlwaysOn"] = UpdateAlwaysOn 28 | v["AlwaysOn"] = AlwaysOn 29 | } 30 | return this.put('otp_validate', v) 31 | } 32 | 33 | statusOTP(name='admin') { 34 | return this.get(`otp_status?name=${name}`) 35 | } 36 | } 37 | 38 | export const authAPI = new APIAuth() 39 | -------------------------------------------------------------------------------- /frontend/src/api/CoreDNS.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APICoreDNS extends API { 4 | constructor() { 5 | super('/') 6 | } 7 | 8 | config() { 9 | return this.get('dnsSettings') 10 | } 11 | setConfig(data) { 12 | return this.put('dnsSettings', data) 13 | } 14 | } 15 | 16 | export const CoreDNS = new APICoreDNS() 17 | -------------------------------------------------------------------------------- /frontend/src/api/Db.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIDb extends API { 4 | constructor() { 5 | super('/plugins/db/') 6 | } 7 | 8 | config() { 9 | return this.get('config') 10 | } 11 | 12 | setConfig(config) { 13 | return this.put('config', config) 14 | } 15 | 16 | stats(bucket = null) { 17 | return this.get(bucket ? `stats/${bucket}` : 'stats') 18 | } 19 | 20 | buckets() { 21 | return this.get('buckets') 22 | } 23 | 24 | deleteBucket(bucket) { 25 | return this.delete(`bucket/${bucket}`) 26 | } 27 | 28 | items(bucket, params = {}) { 29 | return this.get(`items/${bucket}?${new URLSearchParams(params)}`) 30 | } 31 | 32 | getItem(bucket, key) { 33 | return this.get(`/bucket/${bucket}/${key}`) 34 | } 35 | 36 | deleteItem(bucket, key) { 37 | return this.delete(`/bucket/${bucket}/${key}`) 38 | } 39 | 40 | putItem(bucket, key, entry) { 41 | return this.put(`/bucket/${bucket}/${key}`, entry) 42 | } 43 | 44 | } 45 | 46 | export const dbAPI = new APIDb() 47 | -------------------------------------------------------------------------------- /frontend/src/api/Dyndns.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIDynDns extends API { 4 | constructor() { 5 | super('/plugins/dyndns/') 6 | } 7 | 8 | config(){ return this.get('config') } 9 | setConfig(data){ return this.put('config', data) } 10 | refresh(){ return this.get('refresh') } 11 | } 12 | 13 | export const dyndnsAPI = new APIDynDns() 14 | -------------------------------------------------------------------------------- /frontend/src/api/Group.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIGroup extends API { 4 | constructor() { 5 | super('') 6 | } 7 | 8 | list() { return this.get('/groups') } 9 | groups() { return this.get('/groups').then((res) => res.map((g) => g.Name)) } 10 | deleteGroup(name) { return this.delete('/groups', {Name: name})} 11 | } 12 | 13 | export const groupDescriptions = { 14 | dns: 'Outbound DNS Access', 15 | wan: 'Outbound Internet Access', 16 | lan: 'LAN access', 17 | isolated: 18 | 'No access. By default devices without a group are treated as isolated' 19 | } 20 | 21 | export const groupAPI = new APIGroup() 22 | -------------------------------------------------------------------------------- /frontend/src/api/Logs.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APILogs extends API { 4 | constructor() { 5 | super('/') 6 | } 7 | 8 | latest(){ return this.get('logs') } 9 | } 10 | 11 | export const logsAPI = new APILogs() 12 | -------------------------------------------------------------------------------- /frontend/src/api/Multicast.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIMulticast extends API { 4 | constructor() { 5 | super('/') 6 | } 7 | 8 | config(){ return this.get('multicastSettings') } 9 | setConfig(data){ return this.put('multicastSettings', data) } 10 | } 11 | 12 | export const Multicast = new APIMulticast() 13 | -------------------------------------------------------------------------------- /frontend/src/api/Nfmap.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APINfmap extends API { 4 | constructor() { 5 | super('/nfmap') 6 | } 7 | 8 | translate(n) { 9 | if (['wan', 'dns', 'lan', 'dhcp'].includes(n)) { 10 | n = n.replace('wan', 'internet') 11 | return `${n}_access` 12 | } 13 | 14 | return n 15 | } 16 | 17 | getNFVerdictMap(group) { 18 | return this.get('/' + this.translate(group)).then((v) => { 19 | let vmap = v.nftables[1].map 20 | let results = [] 21 | if (vmap.elem && vmap.type) { 22 | for (const device of vmap.elem) { 23 | let info = {} 24 | let i = 0 25 | for (const t of vmap.type) { 26 | info[t] = device[0].concat[i] 27 | i += 1 28 | } 29 | results.push(info) 30 | } 31 | } 32 | 33 | return results 34 | }) 35 | } 36 | } 37 | 38 | export const nfmapAPI = new APINfmap() 39 | -------------------------------------------------------------------------------- /frontend/src/api/Notifications.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APINotifications extends API { 4 | constructor() { 5 | super('') 6 | } 7 | 8 | list() { 9 | return this.get('/notifications') 10 | } 11 | 12 | add(data) { 13 | return this.put('/notifications', data) 14 | } 15 | 16 | update(index, data) { 17 | return this.put(`/notifications/${index}`, data) 18 | } 19 | 20 | remove(index) { 21 | return this.delete(`/notifications/${index}`, {}) 22 | } 23 | } 24 | 25 | export const notificationsAPI = new APINotifications() 26 | -------------------------------------------------------------------------------- /frontend/src/api/Plugin.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIPlugin extends API { 4 | constructor() { 5 | super('') 6 | } 7 | 8 | list() { 9 | return this.get('/plugins') 10 | } 11 | add(data) { 12 | return this.put(`/plugins/${data.Name}`, data) 13 | } 14 | update(data) { 15 | return this.add(data) 16 | } 17 | remove(data) { 18 | return this.delete(`/plugins/${data.Name}`, data) 19 | } 20 | getPlusToken() { 21 | return this.get('/plusToken') 22 | } 23 | validPlusToken() { 24 | return this.get('/plusTokenValid') 25 | } 26 | setPlusToken(data) { 27 | return this.put('/plusToken', data) 28 | } 29 | stopPlusExtension(name) { 30 | return this.put(`/stopPlusExtension`, name) 31 | } 32 | startPlusExtension(name) { 33 | return this.put(`/startPlusExtension`, name) 34 | } 35 | } 36 | 37 | export const pluginAPI = new APIPlugin() 38 | -------------------------------------------------------------------------------- /frontend/src/api/Traffic.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APITraffic extends API { 4 | constructor() { 5 | super('') 6 | } 7 | 8 | traffic(name = null) { 9 | if (name) { 10 | return this.map(name) 11 | } 12 | 13 | return this.get('/iptraffic') 14 | } 15 | 16 | map(name){ return this.get(`/traffic/${name}`)} 17 | history(){ return this.get('/traffic_history') } 18 | } 19 | 20 | export const trafficAPI = new APITraffic() 21 | -------------------------------------------------------------------------------- /frontend/src/api/Wireguard.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export class APIWireguard extends API { 4 | constructor() { 5 | super('/plugins/wireguard/') 6 | } 7 | 8 | //config = () => this.get('/config') 9 | peers() { 10 | return this.get('peers') 11 | } 12 | status() { 13 | return this.get('status') 14 | } 15 | genKey() { 16 | return this.get('genkey') 17 | } 18 | putPeer(data) { 19 | return this.put('peer', data) 20 | } 21 | deletePeer(data) { 22 | return this.delete('peer', data) 23 | } 24 | up() { 25 | return this.put('up') 26 | } 27 | down() { 28 | return this.put('down') 29 | } 30 | 31 | getEndpoints() { 32 | return this.get('endpoints') 33 | } 34 | 35 | setEndpoints(endpoints) { 36 | return this.put('endpoints', endpoints) 37 | } 38 | } 39 | 40 | export const wireguardAPI = new APIWireguard() 41 | -------------------------------------------------------------------------------- /frontend/src/api/index.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export default API 4 | export { 5 | api, 6 | testLogin, 7 | saveLogin, 8 | getApiURL, 9 | setApiURL, 10 | getApiHostname, 11 | getWsURL, 12 | setJWTOTPHeader, 13 | setAuthReturn, 14 | getAuthReturn 15 | } from './API' 16 | export { dbAPI } from './Db' 17 | export { deviceAPI } from './Device' 18 | export { groupAPI } from './Group' 19 | export { wifiAPI } from './Wifi' 20 | export { trafficAPI } from './Traffic' 21 | export { blockAPI, logAPI } from './DNS' 22 | export { nfmapAPI } from './Nfmap' 23 | export { wireguardAPI } from './Wireguard' 24 | export { logsAPI } from './Logs' 25 | export { pluginAPI } from './Plugin' 26 | export { firewallAPI } from './Firewall' 27 | export { authAPI } from './Auth' 28 | export { pfwAPI } from './Pfw' 29 | export { notificationsAPI } from './Notifications' 30 | export { alertsAPI } from './Alerts' 31 | export { meshAPI } from './mesh' 32 | -------------------------------------------------------------------------------- /frontend/src/api/mesh.js: -------------------------------------------------------------------------------- 1 | import API from './API' 2 | 3 | export default class APIMesh extends API { 4 | constructor() { 5 | super('/plugins/mesh/') 6 | } 7 | 8 | config() { 9 | return this.get(`config`); 10 | } 11 | 12 | leafMode() { 13 | return this.get(`leafMode`); 14 | } 15 | 16 | setLeafMode(mode) { 17 | return this.put(`leafMode/${mode}`); 18 | } 19 | 20 | leafRouters() { 21 | return this.get(`leafRouters`); 22 | } 23 | 24 | addLeafRouter(data) { 25 | return this.put(`leafRouter`, data); 26 | } 27 | 28 | delLeafRouter(data) { 29 | data.TLSCA="" 30 | return this.delete(`leafRouter`, data); 31 | } 32 | 33 | setParentCredentials(data) { 34 | return this.put(`setParentCredentials`, data); 35 | } 36 | 37 | setSSID(data) { 38 | return this.put(`setSSID`, data); 39 | } 40 | 41 | syncOTP() { 42 | return this.put(`syncOTP`, null); 43 | } 44 | 45 | meshIter(protocb) { 46 | return this.leafRouters().then((routers) => { 47 | if (routers == null) { 48 | return 49 | } 50 | let apis = [] 51 | for (let i = 0; i < routers.length; i++) { 52 | let r = protocb() 53 | r.setRemoteURL(window.location.protocol + '//' + routers[i].IP + '/') 54 | r.setAuthTokenHeaders(routers[i].APIToken) 55 | apis.push(r) 56 | } 57 | return apis 58 | }) 59 | } 60 | } 61 | 62 | export const meshAPI = new APIMesh() 63 | -------------------------------------------------------------------------------- /frontend/src/assets/fonts/nucleo-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/src/assets/fonts/nucleo-icons.eot -------------------------------------------------------------------------------- /frontend/src/assets/fonts/nucleo-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/src/assets/fonts/nucleo-icons.ttf -------------------------------------------------------------------------------- /frontend/src/assets/fonts/nucleo-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/src/assets/fonts/nucleo-icons.woff -------------------------------------------------------------------------------- /frontend/src/assets/fonts/nucleo-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/src/assets/fonts/nucleo-icons.woff2 -------------------------------------------------------------------------------- /frontend/src/assets/img/bg/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/src/assets/img/bg/.DS_Store -------------------------------------------------------------------------------- /frontend/src/assets/img/bg/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spr-networks/super/1161bfa1350749a3fe67c0ca0c8914216f7b3863/frontend/src/assets/img/bg/bg.jpg -------------------------------------------------------------------------------- /frontend/src/components/Alerts/AlertChart.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | 3 | //empty for iOS, default 4 | const AlertChart = (props) => { 5 | return <> 6 | } 7 | 8 | export default AlertChart 9 | -------------------------------------------------------------------------------- /frontend/src/components/Alerts/EventTimelineChart.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | 3 | //empty for iOS, default 4 | const EventTimelineChart = (props) => { 5 | return <> 6 | } 7 | 8 | export default EventTimelineChart 9 | -------------------------------------------------------------------------------- /frontend/src/components/ColorPicker.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | 3 | import { HStack, Pressable } from '@gluestack-ui/themed' 4 | import { Tooltip } from './Tooltip' 5 | 6 | export default ({ value, onChange, ...props }) => { 7 | const [selected, setSelected] = useState(null) 8 | 9 | useEffect(() => { 10 | setSelected(value) 11 | }, []) 12 | 13 | useEffect(() => { 14 | if (selected != value && onChange) { 15 | onChange(selected) 16 | } 17 | }, [selected]) 18 | 19 | let colors = [ 20 | 'violet', 21 | 'purple', 22 | 'fuchsia', 23 | 'pink', 24 | 'red', 25 | 'tertiary', 26 | 'teal', 27 | 'cyan', 28 | 'blueGray', 29 | 'amber', 30 | 'orange' 31 | ] 32 | 33 | const onPress = (color) => { 34 | setSelected(color) 35 | } 36 | 37 | return ( 38 | 39 | {colors.map((c) => ( 40 | 41 | onPress(c)} 49 | /> 50 | 51 | ))} 52 | 53 | ) 54 | } 55 | -------------------------------------------------------------------------------- /frontend/src/components/DNS/DNSChart.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | 3 | //empty for iOS, default 4 | const DNSChart = (props) => { 5 | return ( 6 | ) 7 | } 8 | 9 | export default DNSChart 10 | -------------------------------------------------------------------------------- /frontend/src/components/DNS/DNSOverrideList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import { FlatList, Text } from '@gluestack-ui/themed' 5 | 6 | import ListHeader from 'components/List/ListHeader' 7 | import DNSOverrideListItem from './DNSOverrideListItem' 8 | 9 | const DNSOverrideList = ({ title, listName, list, deleteListItem, ...props }) => { 10 | return ( 11 | <> 12 | 13 | {props.renderHeader ? props.renderHeader() : null} 14 | 15 | 16 | {!list || !list.length ? ( 17 | {`No ${title.split(' ')[0]} rules configured`} 18 | ) : null} 19 | ( 22 | 23 | )} 24 | keyExtractor={(item) => item.Domain} 25 | /> 26 | 27 | ) 28 | } 29 | 30 | DNSOverrideList.propTypes = { 31 | title: PropTypes.string.isRequired, 32 | listName: PropTypes.string.isRequired, 33 | list: PropTypes.array, 34 | notifyChange: PropTypes.func, 35 | deleteListItem: PropTypes.func 36 | } 37 | 38 | export default DNSOverrideList 39 | -------------------------------------------------------------------------------- /frontend/src/components/DatePicker.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const DatePicker = ({ value, onChange }) => { 5 | return <> 6 | } 7 | 8 | DatePicker.propTypes = { 9 | value: PropTypes.string, 10 | onChange: PropTypes.func 11 | } 12 | 13 | export default DatePicker 14 | -------------------------------------------------------------------------------- /frontend/src/components/DatePicker.web.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import { Input, InputField } from '@gluestack-ui/themed' 5 | 6 | const DatePicker = ({ value, onChange, type, ...props }) => { 7 | const inputRef = React.useRef(null) 8 | 9 | const setValue = (value) => { 10 | let defaultValue = new Date().toString() 11 | let v = value || defaultValue 12 | let splitChar = v.includes('T') ? 'T' : ' ' 13 | v = v.split(splitChar)[0] 14 | inputRef.current.setAttribute('value', v) 15 | } 16 | 17 | useEffect(() => { 18 | if (inputRef?.current) { 19 | inputRef.current.setAttribute('type', type || 'date') 20 | setValue(value) 21 | } 22 | }, [inputRef?.current]) 23 | 24 | useEffect(() => { 25 | setValue(value) 26 | }, [value]) 27 | 28 | return ( 29 | 30 | onChange(e.target.value)} 34 | /> 35 | 36 | ) 37 | } 38 | 39 | DatePicker.propTypes = { 40 | value: PropTypes.string, 41 | onChange: PropTypes.func, 42 | type: PropTypes.string 43 | } 44 | 45 | export default DatePicker 46 | -------------------------------------------------------------------------------- /frontend/src/components/Devices/DeviceExpiry.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import InputSelect from 'components/InputSelect' 4 | 5 | const DeviceExpiry = ({ value, onChange, ...props }) => { 6 | const expirationOptions = [ 7 | { label: 'Never', value: 0 }, 8 | { label: '1 Hour', value: 60 * 60 }, 9 | { label: '1 Day', value: 60 * 60 * 24 }, 10 | { label: '1 Week', value: 60 * 60 * 24 * 7 }, 11 | { label: '4 Weeks', value: 60 * 60 * 24 * 7 * 4 } 12 | ] 13 | 14 | return ( 15 | 0 ? new Date(value * 1e3).toUTCString() : 'Never'} 19 | onChange={(v) => onChange(parseInt(v))} 20 | /> 21 | ) 22 | } 23 | 24 | export default DeviceExpiry 25 | 26 | DeviceExpiry.propTypes = { 27 | value: PropTypes.number, 28 | onChange: PropTypes.func 29 | } 30 | -------------------------------------------------------------------------------- /frontend/src/components/Devices/DeviceList.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { FlatList } from '@gluestack-ui/themed' 4 | 5 | import {Device} from 'components/Devices/Device' 6 | 7 | const DeviceList = ({ list, deleteListItem, notifyChange, ...props }) => { 8 | const renderItem = ({ item }) => ( 9 | 15 | ) 16 | 17 | return ( 18 | 24 | ) 25 | } 26 | 27 | export default DeviceList 28 | -------------------------------------------------------------------------------- /frontend/src/components/Devices/DeviceQRCode.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import PropTypes from 'prop-types' 3 | import QRCode from 'react-qr-code' 4 | 5 | import { Box } from '@gluestack-ui/themed' 6 | 7 | // NOTE issues with type on ios: wpa2 vs wpa3, WPA try both 8 | const generateQRCode = (_ssid, password, type, hidden = false) => { 9 | type = 'WPA' //type.toUpperCase() 10 | return `WIFI:S:${_ssid};P:${password};T:${type};${hidden};` 11 | } 12 | 13 | // note can have multiple ssids 14 | const DeviceQRCode = ({ ssid, psk, type, ...props }) => { 15 | const [value, setValue] = useState(null) 16 | 17 | useEffect(() => { 18 | //device.PSKEntry.Psk, device.PSKEntry.Type 19 | let value = generateQRCode(ssid, psk, type) 20 | setValue(value) 21 | }, [ssid, psk, type]) 22 | 23 | if (!value) { 24 | return <> 25 | } 26 | 27 | return ( 28 | 29 | 30 | 31 | ) 32 | } 33 | 34 | // 35 | 36 | DeviceQRCode.propTypes = { 37 | //device: PropTypes.object.isRequired, 38 | psk: PropTypes.string.isRequired, 39 | type: PropTypes.string, 40 | ssid: PropTypes.string.isRequired 41 | } 42 | 43 | export default DeviceQRCode 44 | -------------------------------------------------------------------------------- /frontend/src/components/Firewall/ICMP.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | 3 | import { firewallAPI } from 'api' 4 | import { alertState } from 'AppContext' 5 | 6 | import { VStack, Switch, Text } from '@gluestack-ui/themed' 7 | 8 | import { ListHeader, ListItem } from 'components/List' 9 | 10 | const ICMP = (props) => { 11 | const [status, setStatus] = useState({ PingLan: false, PingWan: false }) 12 | 13 | useEffect(() => { 14 | firewallAPI.config().then((config) => { 15 | setStatus({ PingLan: config.PingLan, PingWan: config.PingWan }) 16 | }) 17 | }, []) 18 | 19 | const togglePing = (key) => { 20 | let updated = { ...status, [key]: !status[key] } 21 | firewallAPI 22 | .setICMP(updated) 23 | .then(() => { 24 | alertState.success('Updated Ping Settings') 25 | }) 26 | .catch((err) => alertState.error(err)) 27 | 28 | setStatus(updated) 29 | } 30 | 31 | return ( 32 | 33 | 37 | 38 | {['PingLan', 'PingWan'].map((d) => ( 39 | 40 | {d.replace(/^Ping/, '').toUpperCase()} 41 | 42 | togglePing(d)} /> 43 | 44 | ))} 45 | 46 | ) 47 | } 48 | 49 | export default ICMP 50 | -------------------------------------------------------------------------------- /frontend/src/components/Flow/AddFlowCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import { Dimensions } from 'react-native' 4 | 5 | import { FlowCard } from './FlowCard' 6 | import { getCards } from './FlowCards' 7 | 8 | import { FlatList, Pressable, VStack, Text } from '@gluestack-ui/themed' 9 | 10 | // TODO we should just list available flow cards for cardType here 11 | 12 | //type = trigger or action 13 | const AddFlowCard = ({ cardType, onSubmit, ...props }) => { 14 | const handleSelect = (item) => { 15 | onSubmit(item) 16 | } 17 | 18 | const cards = getCards(cardType) 19 | 20 | let numColumns = Dimensions.get('window').width > 1024 ? 2 : 1 21 | let cardWidth = numColumns == 2 ? '$1/2' : '$full' 22 | 23 | //TODO only if Device.width>=1024 24 | return ( 25 | 26 | item.title} 30 | renderItem={({ item }) => ( 31 | handleSelect(item)} w={cardWidth} px="$1"> 32 | 33 | 34 | )} 35 | /> 36 | 37 | ) 38 | } 39 | 40 | AddFlowCard.propTypes = { 41 | cardType: PropTypes.string.isRequired, 42 | onSubmit: PropTypes.func.isRequired 43 | } 44 | 45 | export default AddFlowCard 46 | -------------------------------------------------------------------------------- /frontend/src/components/Form/ProtocolRadio.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { 4 | Radio, 5 | RadioGroup, 6 | RadioIndicator, 7 | RadioIcon, 8 | RadioLabel, 9 | HStack, 10 | CircleIcon 11 | } from '@gluestack-ui/themed' 12 | 13 | const ProtocolRadio = ({ value, onChange, ...props }) => { 14 | return ( 15 | 21 | 22 | 23 | 24 | 25 | 26 | TCP 27 | 28 | 29 | 30 | 31 | 32 | UDP 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | export default ProtocolRadio 40 | -------------------------------------------------------------------------------- /frontend/src/components/IPInfo.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import { wifiAPI } from 'api' 5 | import { AlertContext } from 'layouts/Admin' 6 | 7 | const IPInfo = (props) => { 8 | const contextType = useContext(AlertContext) 9 | 10 | const handleClick = (e) => { 11 | let ip = e.target.innerText 12 | wifiAPI 13 | .asn(ip) 14 | .then((asn) => { 15 | contextType.alert( 16 | 'IP ASN information', 17 | <> 18 |
19 | {asn.ASN} 20 |
21 |
22 | {asn.Name} 23 |
24 |
25 | {asn.Country} 26 |
27 | {/**/} 32 | 33 | ) 34 | }) 35 | .catch((err) => { 36 | contextType.error('IP info', `No ASN info for IP address: ${ip}`) 37 | }) 38 | } 39 | return ( 40 | {props.value || props.ip || props.children} 41 | ) 42 | } 43 | 44 | IPInfo.propTypes = {} 45 | 46 | export default IPInfo 47 | -------------------------------------------------------------------------------- /frontend/src/components/List/AddButton.js: -------------------------------------------------------------------------------- 1 | //TODO 2 | import React from 'react' 3 | 4 | import { Button, ButtonIcon, ButtonText, AddIcon } from '@gluestack-ui/themed' 5 | 6 | const AddButton = ({ onPress, ...props }) => { 7 | return ( 8 | 19 | ) 20 | } 21 | export default AddButton 22 | -------------------------------------------------------------------------------- /frontend/src/components/List/ListItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Box, HStack } from '@gluestack-ui/themed' 4 | 5 | const ListItem = ({ p, children, ...props }) => { 6 | return ( 7 | 23 | {children} 24 | 25 | ) 26 | } 27 | 28 | export default ListItem 29 | -------------------------------------------------------------------------------- /frontend/src/components/List/index.js: -------------------------------------------------------------------------------- 1 | import ListHeader from './ListHeader' 2 | import ListItem from './ListItem' 3 | 4 | export { ListHeader, ListItem } 5 | -------------------------------------------------------------------------------- /frontend/src/components/Menu.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { Menu, MenuItem, MenuItemLabel } from '@gluestack-ui/themed' 4 | 5 | const TestMenu = (props) => { 6 | return ( 7 | alert(`Testmenu: ${e.currentKey} selected`)} 12 | > 13 | 14 | Delete 15 | 16 | 17 | ) 18 | } 19 | 20 | //Menu, Menu.Item 21 | TestMenu.Item = MenuItem 22 | 23 | let MenuExport = Menu 24 | 25 | export default MenuExport 26 | 27 | export { MenuExport as Menu, MenuItem, MenuItemLabel } 28 | -------------------------------------------------------------------------------- /frontend/src/components/Pagination.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import { 5 | Button, 6 | ButtonText, 7 | ButtonIcon, 8 | HStack, 9 | ArrowLeftIcon, 10 | ArrowRightIcon 11 | } from '@gluestack-ui/themed' 12 | 13 | const Pagination = ({ 14 | page, 15 | perPage, 16 | total, 17 | onChange, 18 | onPrevPage, 19 | onNextPage, 20 | ...props 21 | }) => { 22 | return ( 23 | 24 | 33 | 42 | 43 | ) 44 | } 45 | 46 | Pagination.propTypes = { 47 | page: PropTypes.number.isRequired, 48 | pages: PropTypes.number.isRequired, 49 | perPage: PropTypes.number.isRequired, 50 | onChange: PropTypes.func.isRequired 51 | } 52 | 53 | export default Pagination 54 | -------------------------------------------------------------------------------- /frontend/src/components/Plugins/CustomPlugin.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from 'react' 2 | 3 | export default () => { 4 | return <> 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/components/Select.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { 4 | Icon, 5 | Select as SelectGS, 6 | SelectTrigger, 7 | SelectInput, 8 | SelectIcon, 9 | SelectPortal, 10 | SelectBackdrop, 11 | SelectContent, 12 | SelectDragIndicatorWrapper, 13 | SelectDragIndicator, 14 | SelectItem, 15 | SelectScrollView, 16 | ChevronDownIcon 17 | } from '@gluestack-ui/themed' 18 | 19 | /* 20 | //import { Select as NBSelect } //OLD 21 | 22 | const Select = NBSelect 23 | export default Select 24 | 25 | export { Select } 26 | */ 27 | 28 | const Select = (props) => { 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {props.children} 44 | 45 | 46 | 47 | ) 48 | } 49 | 50 | // behave like old Select 51 | Select.Item = SelectItem 52 | 53 | export { Select, SelectItem } 54 | -------------------------------------------------------------------------------- /frontend/src/components/Select.web.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import { 4 | Icon, 5 | Select as SelectGS, 6 | SelectTrigger, 7 | SelectInput, 8 | SelectIcon, 9 | SelectPortal, 10 | SelectBackdrop, 11 | SelectContent, 12 | SelectDragIndicatorWrapper, 13 | SelectDragIndicator, 14 | SelectItem, 15 | ChevronDownIcon 16 | } from '@gluestack-ui/themed' 17 | 18 | /* 19 | //import { Select as NBSelect } //OLD 20 | 21 | const Select = (props) => { 22 | const isSafari = () => 23 | /Safari/.test(navigator.userAgent) && 24 | /Apple Computer/.test(navigator.vendor); 25 | 26 | 27 | return ( 28 | 29 | ); 30 | }; 31 | 32 | Select.Item = NBSelect.Item 33 | export default Select 34 | export { Select } 35 | */ 36 | 37 | const Select = (props) => { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | {props.children} 53 | 54 | 55 | 56 | ) 57 | } 58 | 59 | // behave like old Select 60 | Select.Item = SelectItem 61 | 62 | export { Select, SelectItem } 63 | -------------------------------------------------------------------------------- /frontend/src/components/SwipeListView/index.js: -------------------------------------------------------------------------------- 1 | import SwipeListView from './SwipeListView' 2 | import SwipeRow from './SwipeRow' 3 | 4 | export { SwipeListView, SwipeRow } 5 | -------------------------------------------------------------------------------- /frontend/src/components/Toggle.css: -------------------------------------------------------------------------------- 1 | .switch { 2 | position: relative; 3 | display: inline-block; 4 | width: 60px; 5 | height: 34px; 6 | height: 1.875rem; 7 | width: 3.65rem; 8 | line-height: normal; 9 | margin-top: 5px; 10 | margin-bottom: -5px; 11 | outline: none; 12 | } 13 | .switch input { 14 | position: absolute; 15 | top: -99999px; 16 | left: -99999px; 17 | } 18 | .slider { 19 | position: absolute; 20 | cursor: pointer; 21 | top: 0; 22 | left: 0; 23 | right: 0; 24 | bottom: 0; 25 | background-color: #ccc; 26 | -webkit-transition: .4s; 27 | transition: .4s; 28 | border-radius: 34px; 29 | } 30 | .slider:before { 31 | position: absolute; 32 | content: ""; 33 | height: 24px; 34 | width: 24px; 35 | left: 4px; 36 | bottom: 3px; 37 | background-color: white; 38 | -webkit-transition: .4s; 39 | transition: .4s; 40 | border-radius: 50%; 41 | } 42 | input:checked + .slider { 43 | background-color: #51bcda; 44 | } 45 | input:focus + .slider { 46 | box-shadow: 0 0 1px #51bcda; 47 | } 48 | input:checked + .slider:before { 49 | -webkit-transform: translateX(26px); 50 | -ms-transform: translateX(26px); 51 | transform: translateX(26px); 52 | } 53 | -------------------------------------------------------------------------------- /frontend/src/components/TokenItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Platform } from 'react-native' 3 | import PropTypes from 'prop-types' 4 | 5 | import { 6 | Button, 7 | ButtonIcon, 8 | ButtonText, 9 | Tooltip, 10 | TooltipContent, 11 | TooltipText, 12 | CopyIcon 13 | } from '@gluestack-ui/themed' 14 | 15 | import { copy } from 'utils' 16 | 17 | const TokenItem = ({ token, ...props }) => { 18 | const showClipboard = true //Platform.OS !== 'web' || navigator.clipboard 19 | 20 | return ( 21 | { 25 | return ( 26 | 37 | ) 38 | }} 39 | > 40 | 41 | {token} 42 | 43 | 44 | ) 45 | } 46 | export default React.memo(TokenItem) 47 | 48 | export { TokenItem } 49 | 50 | TokenItem.propTypes = { 51 | token: PropTypes.string 52 | } 53 | -------------------------------------------------------------------------------- /frontend/src/components/Tooltip.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import { 5 | Pressable, 6 | Tooltip, 7 | TooltipContent, 8 | TooltipText 9 | } from '@gluestack-ui/themed' 10 | 11 | const T = ({ label, children, ...props }) => { 12 | return ( 13 | { 17 | return ( 18 | 19 | {children} 20 | 21 | ) 22 | }} 23 | > 24 | 25 | {label} 26 | 27 | 28 | ) 29 | } 30 | 31 | export default T 32 | export { T as Tooltip } 33 | 34 | T.propTypes = { 35 | label: PropTypes.string 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/components/Traffic/TimeSeriesChart.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | const TimeSeriesChart = (props) => { 5 | return <> 6 | } 7 | 8 | TimeSeriesChart.propTypes = { 9 | type: PropTypes.string, 10 | title: PropTypes.string, 11 | data: PropTypes.object, 12 | handleTimeChange: PropTypes.func, 13 | onClick: PropTypes.func 14 | } 15 | 16 | export default TimeSeriesChart 17 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | // web app entrypoint 2 | import React from 'react' 3 | import { createRoot } from 'react-dom/client' 4 | //import ReactDOM from 'react-dom' 5 | 6 | import App from './App' 7 | 8 | const container = document.getElementById('root') 9 | const root = createRoot(container) 10 | root.render() 11 | 12 | //ReactDOM.render(, document.getElementById('root')) 13 | -------------------------------------------------------------------------------- /frontend/src/layouts/Auth.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SafeAreaView } from 'react-native' 3 | import { Outlet } from 'react-router-dom' 4 | import { View, useColorMode } from '@gluestack-ui/themed' 5 | 6 | const AuthLayout = () => { 7 | let colorMode = useColorMode() 8 | 9 | const backgroundColor = 10 | colorMode === 'light' ? '$warmGray100' : '$blueGray900' 11 | 12 | return ( 13 | 25 | 30 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default AuthLayout 37 | -------------------------------------------------------------------------------- /frontend/src/layouts/Auth.web.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Outlet } from 'react-router-dom' 3 | import Footer from 'components/Footer/Footer' 4 | import { Box, Image, View, useColorMode } from '@gluestack-ui/themed' 5 | 6 | const AuthLayout = () => { 7 | const colorMode = useColorMode() 8 | 9 | return ( 10 | 20 | 21 | 30 | 31 | 32 |