From 391727f269d29de2fe588c990bb98c8afbd99ceb Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:39:23 -0400 Subject: [PATCH 01/61] Limit the Skychay UI to localhost --- cmd/apps/skychat/skychat.go | 2 +- .../pages/node/apps/node-apps-list/node-apps-list.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/apps/skychat/skychat.go b/cmd/apps/skychat/skychat.go index b35bfb317..876225a11 100644 --- a/cmd/apps/skychat/skychat.go +++ b/cmd/apps/skychat/skychat.go @@ -97,7 +97,7 @@ func main() { } setAppStatus(appCl, appserver.AppDetailedStatusRunning) srv := &http.Server{ //nolint gosec - Addr: *addr, + Addr: "127.0.0.1" + (*addr), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts index d895368c7..fc8edf410 100644 --- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts +++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts @@ -264,7 +264,7 @@ export class NodeAppsListComponent implements OnInit, OnDestroy { port = ':' + port; } - return 'http://' + this.nodeIp + port; + return 'http://127.0.0.1' + port; } else if (app.name.toLocaleLowerCase() === 'vpn-client' && this.nodePK) { return location.origin + '/#/vpn/' + this.nodePK + '/status'; } else if (!this.officialAppsList.has(app.name)) { From 34179fecc4662d1155b7778d5ace6fbb3b54a117 Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:44:18 -0400 Subject: [PATCH 02/61] Settings for Skychat --- cmd/apps/skychat/skychat.go | 19 ++- pkg/visor/api.go | 42 +++++++ pkg/visor/hypervisor.go | 10 +- pkg/visor/rpc.go | 7 ++ pkg/visor/rpc_client.go | 23 ++++ .../skywire-manager-src/src/app/app.module.ts | 2 + .../node-apps-list.component.ts | 26 ++-- .../skychat-settings.component.html | 42 +++++++ .../skychat-settings.component.scss | 0 .../skychat-settings.component.ts | 119 ++++++++++++++++++ .../src/assets/i18n/en.json | 8 ++ .../src/assets/i18n/es.json | 8 ++ .../src/assets/i18n/es_base.json | 8 ++ 13 files changed, 302 insertions(+), 12 deletions(-) create mode 100644 static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.html create mode 100644 static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.scss create mode 100644 static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts diff --git a/cmd/apps/skychat/skychat.go b/cmd/apps/skychat/skychat.go index 876225a11..8ed778a17 100644 --- a/cmd/apps/skychat/skychat.go +++ b/cmd/apps/skychat/skychat.go @@ -36,7 +36,7 @@ const ( port = routing.Port(1) ) -var addr = flag.String("addr", ":8001", "address to bind") +var addr = flag.String("addr", ":8001", "address to bind, put an * before the port if you want to be able to access outside localhost") var r = netutil.NewRetrier(nil, 50*time.Millisecond, netutil.DefaultMaxBackoff, 5, 2) var ( @@ -84,7 +84,20 @@ func main() { http.HandleFunc("/message", messageHandler(ctx)) http.HandleFunc("/sse", sseHandler) - fmt.Println("Serving HTTP on", *addr) + url := "" + address := *addr + if len(address) < 5 || (address[:1] != ":" && address[:2] != "*:") { + url = "127.0.0.1:8001" + } else if address[:1] == ":" { + url = "127.0.0.1" + address + } else if address[:2] == "*:" { + url = address[1:] + } else { + url = "127.0.0.1:8001" + } + + fmt.Println("Serving HTTP on", url) + if runtime.GOOS != "windows" { termCh := make(chan os.Signal, 1) signal.Notify(termCh, os.Interrupt) @@ -97,7 +110,7 @@ func main() { } setAppStatus(appCl, appserver.AppDetailedStatusRunning) srv := &http.Server{ //nolint gosec - Addr: "127.0.0.1" + (*addr), + Addr: url, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 5a1d88084..6c483eaa8 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -12,6 +12,7 @@ import ( "net/http" "os" "path/filepath" + "strconv" "strings" "sync/atomic" "time" @@ -72,6 +73,7 @@ type API interface { SetAppPassword(appName, password string) error SetAppPK(appName string, pk cipher.PubKey) error SetAppSecure(appName string, isSecure bool) error + SetAppAddress(appName string, address string) error SetAppKillswitch(appName string, killswitch bool) error SetAppNetworkInterface(appName string, netifc string) error SetAppDNS(appName string, dnsaddr string) error @@ -769,6 +771,46 @@ func (v *Visor) SetAppSecure(appName string, isSecure bool) error { return nil } +// SetAppAddress implements API. +func (v *Visor) SetAppAddress(appName string, address string) error { + // check app launcher availability + if v.appL == nil { + return ErrAppLauncherNotAvailable + } + + if appName != visorconfig.SkychatName { + return fmt.Errorf("app %s is not allowed to set addr", appName) + } + + if len(address) < 5 || (address[:1] != ":" && address[:2] != "*:") { + return fmt.Errorf("invalid addr value: %s", address) + } + + forLocalhostOnly := address[:1] == ":" + prefix := 2 + if forLocalhostOnly == true { + prefix = 1 + } + + portNumber, err := strconv.Atoi(address[prefix:]) + if err != nil || portNumber < 1025 || portNumber > 65536 { + return fmt.Errorf("invalid port number: %s", strconv.Itoa(portNumber)) + } + + v.log.Infof("Setting %s addr to %v", appName, address) + + const ( + addrArg = "-addr" + ) + if err := v.conf.UpdateAppArg(v.appL, appName, addrArg, address); err != nil { + return err + } + + v.log.Infof("Updated %v addr state", appName) + + return nil +} + // SetAppPK implements API. func (v *Visor) SetAppPK(appName string, pk cipher.PubKey) error { allowedToChangePK := func(appName string) bool { diff --git a/pkg/visor/hypervisor.go b/pkg/visor/hypervisor.go index a797cb87c..cd45bccff 100644 --- a/pkg/visor/hypervisor.go +++ b/pkg/visor/hypervisor.go @@ -598,6 +598,7 @@ func (hv *Hypervisor) putApp() http.HandlerFunc { AutoStart *bool `json:"autostart,omitempty"` Killswitch *bool `json:"killswitch,omitempty"` Secure *bool `json:"secure,omitempty"` + Address *string `json:"Address,omitempty"` Status *int `json:"status,omitempty"` Passcode *string `json:"passcode,omitempty"` NetIfc *string `json:"netifc,omitempty"` @@ -608,7 +609,7 @@ func (hv *Hypervisor) putApp() http.HandlerFunc { shouldRestartApp := func(r req) bool { // we restart the app if one of these fields was changed - return r.Killswitch != nil || r.Secure != nil || r.Passcode != nil || + return r.Killswitch != nil || r.Secure != nil || r.Address != nil || r.Passcode != nil || r.PK != nil || r.NetIfc != nil || r.CustomSetting != nil } @@ -660,6 +661,13 @@ func (hv *Hypervisor) putApp() http.HandlerFunc { } } + if reqBody.Address != nil { + if err := ctx.API.SetAppAddress(ctx.App.Name, *reqBody.Address); err != nil { + httputil.WriteJSON(w, r, http.StatusInternalServerError, err) + return + } + } + if reqBody.NetIfc != nil { if err := ctx.API.SetAppNetworkInterface(ctx.App.Name, *reqBody.NetIfc); err != nil { httputil.WriteJSON(w, r, http.StatusInternalServerError, err) diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 2c008a16a..e6e601401 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -391,6 +391,13 @@ func (r *RPC) SetAppSecure(in *SetAppBoolIn, _ *struct{}) (err error) { return r.visor.SetAppSecure(in.AppName, in.Val) } +// SetAppPort sets addr flag for the app +func (r *RPC) SetAppAddress(in *SetAppStringIn, _ *struct{}) (err error) { + defer rpcutil.LogCall(r.log, "SetAppAddress", in)(nil, &err) + + return r.visor.SetAppAddress(in.AppName, in.Val) +} + // GetAppStats gets app runtime statistics. func (r *RPC) GetAppStats(appName *string, out *appserver.AppStats) (err error) { defer rpcutil.LogCall(r.log, "GetAppStats", appName)(out, &err) diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 86c246c33..af9ea3dbd 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -267,6 +267,14 @@ func (rc *rpcClient) SetAppSecure(appName string, isSecure bool) error { }, &struct{}{}) } +// SetAppAddress implements API. +func (rc *rpcClient) SetAppAddress(appName string, address string) error { + return rc.Call("SetAppAddress", &SetAppStringIn{ + AppName: appName, + Val: address, + }, &struct{}{}) +} + // SetAppDNS implements API. func (rc *rpcClient) SetAppDNS(appName string, dnsAddr string) error { return rc.Call("SetAppDNS", &SetAppStringIn{ @@ -980,6 +988,21 @@ func (mc *mockRPCClient) SetAppSecure(appName string, isSecure bool) error { //n }) } +// SetAppAddress implements API. +func (mc *mockRPCClient) SetAppAddress(appName string, address string) error { //nolint:all + return mc.do(true, func() error { + const chatName = "skychat" + + for i := range mc.o.Apps { + if mc.o.Apps[i].Name == chatName { + return nil + } + } + + return fmt.Errorf("app of name '%s' does not exist", chatName) + }) +} + // SetAppDNS implements API. func (mc *mockRPCClient) SetAppDNS(string, string) error { return mc.do(true, func() error { diff --git a/static/skywire-manager-src/src/app/app.module.ts b/static/skywire-manager-src/src/app/app.module.ts index 36fc08274..36a55e410 100644 --- a/static/skywire-manager-src/src/app/app.module.ts +++ b/static/skywire-manager-src/src/app/app.module.ts @@ -101,6 +101,7 @@ import { RewardsAddressComponent } from './components/pages/node/node-info/node- import { BulkRewardAddressChangerComponent } from './components/layout/bulk-reward-address-changer/bulk-reward-address-changer.component'; import { UserAppSettingsComponent } from './components/pages/node/apps/node-apps/user-app-settings/user-app-settings.component'; import { NodeLogsComponent } from './components/pages/node/actions/node-logs/node-logs.component'; +import { SkychatSettingsComponent } from './components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component'; const globalRippleConfig: RippleGlobalOptions = { disabled: true, @@ -178,6 +179,7 @@ const globalRippleConfig: RippleGlobalOptions = { BulkRewardAddressChangerComponent, UserAppSettingsComponent, NodeLogsComponent, + SkychatSettingsComponent, ], imports: [ BrowserModule, diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts index fc8edf410..38ae2dbed 100644 --- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts +++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/node-apps-list.component.ts @@ -23,6 +23,7 @@ import { FilterProperties, FilterFieldTypes } from 'src/app/utils/filters'; import { SortingColumn, SortingModes, DataSorter } from 'src/app/utils/lists/data-sorter'; import { DataFilterer } from 'src/app/utils/lists/data-filterer'; import { UserAppSettingsComponent } from '../node-apps/user-app-settings/user-app-settings.component'; +import { SkychatSettingsComponent } from '../node-apps/skychat-settings/skychat-settings.component'; /** * Shows the list of applications of a node. It shows official or user apps, not both at the @@ -80,7 +81,7 @@ export class NodeAppsListComponent implements OnInit, OnDestroy { } // List with the names of all the apps which can not be configured directly on the manager. - appsWithoutConfig = new Set(['skychat']); + appsWithoutConfig = new Set(); // All apps the ode has. allApps: Application[]; @@ -248,14 +249,23 @@ export class NodeAppsListComponent implements OnInit, OnDestroy { */ getLink(app: Application): string { if (app.name.toLocaleLowerCase() === 'skychat' && this.nodeIp && app.status !== 0 && app.status !== 2) { - // Default port. + // Default port and ip. let port = '8001'; + let url = '127.0.0.1'; - // Try to get the port from the config array. + // Try to get the address and port from the config array. if (app.args) { for (let i = 0; i < app.args.length; i++) { if (app.args[i] === '-addr' && i + 1 < app.args.length) { - port = (app.args[i + 1] as string).trim(); + const addr = (app.args[i + 1] as string).trim(); + + const parts = addr.split(':'); + // If the app can be accessed outside localhost, use the remote ip. + if (parts[0] === '*') { + url = this.nodeIp; + } + + port = parts[1]; } } } @@ -264,7 +274,7 @@ export class NodeAppsListComponent implements OnInit, OnDestroy { port = ':' + port; } - return 'http://127.0.0.1' + port; + return 'http://' + url + port; } else if (app.name.toLocaleLowerCase() === 'vpn-client' && this.nodePK) { return location.origin + '/#/vpn/' + this.nodePK + '/status'; } else if (!this.officialAppsList.has(app.name)) { @@ -574,12 +584,12 @@ export class NodeAppsListComponent implements OnInit, OnDestroy { * Shows the appropriate modal window for configuring the app. */ config(app: Application): void { - if (app.name === 'skysocks' || app.name === 'vpn-server') { + if (app.name === 'skychat') { + SkychatSettingsComponent.openDialog(this.dialog, app); + } else if (app.name === 'skysocks' || app.name === 'vpn-server') { SkysocksSettingsComponent.openDialog(this.dialog, app); } else if (app.name === 'skysocks-client' || app.name === 'vpn-client') { SkysocksClientSettingsComponent.openDialog(this.dialog, app); - } else if (app.name === 'skychat') { - this.snackbarService.showError('apps.error'); } else { UserAppSettingsComponent.openDialog(this.dialog, app); } diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.html b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.html new file mode 100644 index 000000000..5d53b0fcf --- /dev/null +++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.html @@ -0,0 +1,42 @@ + +
+ + +
+ + + {{ 'common.yes' | translate }} + {{ 'common.no' | translate }} + +
+
+ + +
+ + +
+ + {{ 'apps.skychat-settings.port-error' | translate }} + +
+
+ + + {{ 'apps.skychat-settings.save' | translate }} + +
diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.scss b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts new file mode 100644 index 000000000..49805c784 --- /dev/null +++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts @@ -0,0 +1,119 @@ +import { Component, OnInit, ViewChild, OnDestroy, Inject } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { MatDialogRef, MatDialog, MatDialogConfig, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { Subscription } from 'rxjs'; + +import { ButtonComponent } from '../../../../../layout/button/button.component'; +import { NodeComponent } from '../../../node.component'; +import { SnackbarService } from '../../../../../../services/snackbar.service'; +import { AppConfig } from 'src/app/app.config'; +import { processServiceError } from 'src/app/utils/errors'; +import { OperationError } from 'src/app/utils/operation-error'; +import { AppsService } from 'src/app/services/apps.service'; +import { Application } from 'src/app/app.datatypes'; + +/** + * Modal window used for configuring the Skychat app. + */ +@Component({ + selector: 'app-skychat-settings', + templateUrl: './skychat-settings.component.html', + styleUrls: ['./skychat-settings.component.scss'] +}) +export class SkychatSettingsComponent implements OnInit, OnDestroy { + @ViewChild('button') button: ButtonComponent; + form: UntypedFormGroup; + + private operationSubscription: Subscription; + + /** + * Opens the modal window. Please use this function instead of opening the window "by hand". + */ + public static openDialog(dialog: MatDialog, app: Application): MatDialogRef { + const config = new MatDialogConfig(); + config.data = app; + config.autoFocus = false; + config.width = AppConfig.mediumModalWidth; + + return dialog.open(SkychatSettingsComponent, config); + } + + constructor( + @Inject(MAT_DIALOG_DATA) private data: Application, + private appsService: AppsService, + private formBuilder: UntypedFormBuilder, + public dialogRef: MatDialogRef, + private snackbarService: SnackbarService, + ) { } + + ngOnInit() { + this.form = this.formBuilder.group({ + localhostOnly: [true], + port: ['', Validators.compose([Validators.required, Validators.min(1025), Validators.max(65536)])], + }); + + // Get the current values saved on the visor, if returned by the API. + if (this.data.args && this.data.args.length > 0) { + for (let i = 0; i < this.data.args.length; i++) { + if (this.data.args[i] === '-addr' && i + 1 < this.data.args.length) { + const parts = (this.data.args[i + 1] as string).split(':'); + if (parts[0] === '*') { + this.form.get('localhostOnly').setValue(false); + } + + this.form.get('port').setValue(parts[1]); + } + } + } + } + + ngOnDestroy() { + if (this.operationSubscription) { + this.operationSubscription.unsubscribe(); + } + } + + /** + * If true, all the ways provided by default by the UI for closing the modal window are disabled. + */ + get disableDismiss(): boolean { + return this.button ? this.button.isLoading : false; + } + + /** + * Saves the settings. + */ + saveChanges() { + if (!this.form.valid || this.button.disabled) { + return; + } + + this.button.showLoading(); + + const data = {address: this.form.get('localhostOnly').value ? ':' : '*:'}; + data['address'] += this.form.get('port').value; + + this.operationSubscription = this.appsService.changeAppSettings( + // The node pk is obtained from the currently openned node page. + NodeComponent.getCurrentNodeKey(), + this.data.name, + data, + ).subscribe({ + next: this.onSuccess.bind(this), + error: this.onError.bind(this) + }); + } + + private onSuccess() { + NodeComponent.refreshCurrentDisplayedData(); + this.snackbarService.showDone('apps.skychat-settings.changes-made'); + this.dialogRef.close(); + } + + private onError(err: OperationError) { + this.button.showError(); + err = processServiceError(err); + + this.snackbarService.showError(err); + } +} diff --git a/static/skywire-manager-src/src/assets/i18n/en.json b/static/skywire-manager-src/src/assets/i18n/en.json index 79aa6c832..9f71be51a 100644 --- a/static/skywire-manager-src/src/assets/i18n/en.json +++ b/static/skywire-manager-src/src/assets/i18n/en.json @@ -457,6 +457,14 @@ "empty-confirmation": "The settings list is empty. Do you really want to continue?", "changes-made": "The changes have been made." }, + "skychat-settings": { + "title": "Skychat Settings", + "localhost-only": "Allow access from the local machine only", + "port": "Port", + "save": "Save", + "changes-made": "The changes have been made.", + "port-error": "Must be a valid number between 1025 and 65536." + }, "vpn-socks-server-settings": { "socks-title": "Skysocks Settings", "vpn-title": "VPN-Server Settings", diff --git a/static/skywire-manager-src/src/assets/i18n/es.json b/static/skywire-manager-src/src/assets/i18n/es.json index 27dd44541..ad795e30e 100644 --- a/static/skywire-manager-src/src/assets/i18n/es.json +++ b/static/skywire-manager-src/src/assets/i18n/es.json @@ -461,6 +461,14 @@ "empty-confirmation": "La lista de configuración está vacía. ¿Realmente desea continuar?", "changes-made": "Los cambios han sido realizados." }, + "skychat-settings": { + "title": "Configuración de Skychat", + "localhost-only": "Permitir acceso sólo desde la máquina local", + "port": "Puerto", + "save": "Guardar", + "changes-made": "Los cambios han sido realizados.", + "port-error": "Debe ser un número válido entre 1025 y 65536." + }, "vpn-socks-server-settings": { "socks-title": "Configuración de Skysocks", "vpn-title": "Configuración de VPN-Server", diff --git a/static/skywire-manager-src/src/assets/i18n/es_base.json b/static/skywire-manager-src/src/assets/i18n/es_base.json index 8c853eda9..505872da8 100644 --- a/static/skywire-manager-src/src/assets/i18n/es_base.json +++ b/static/skywire-manager-src/src/assets/i18n/es_base.json @@ -461,6 +461,14 @@ "empty-confirmation": "The settings list is empty. Do you really want to continue?", "changes-made": "The changes have been made." }, + "skychat-settings": { + "title": "Skychat Settings", + "localhost-only": "Allow access from the local machine only", + "port": "Port", + "save": "Save", + "changes-made": "The changes have been made.", + "port-error": "Must be a valid number between 1025 and 65536." + }, "vpn-socks-server-settings": { "socks-title": "Skysocks Settings", "vpn-title": "VPN-Server Settings", From 37e20d9236386b031c6e1a245cc894388a2affea Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:50:24 -0400 Subject: [PATCH 03/61] Small lint fixes --- pkg/visor/api.go | 2 +- pkg/visor/rpc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 6c483eaa8..88d0cceef 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -788,7 +788,7 @@ func (v *Visor) SetAppAddress(appName string, address string) error { forLocalhostOnly := address[:1] == ":" prefix := 2 - if forLocalhostOnly == true { + if forLocalhostOnly { prefix = 1 } diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index e6e601401..699a44cfe 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -391,7 +391,7 @@ func (r *RPC) SetAppSecure(in *SetAppBoolIn, _ *struct{}) (err error) { return r.visor.SetAppSecure(in.AppName, in.Val) } -// SetAppPort sets addr flag for the app +// SetAppAddress sets addr flag for the app func (r *RPC) SetAppAddress(in *SetAppStringIn, _ *struct{}) (err error) { defer rpcutil.LogCall(r.log, "SetAppAddress", in)(nil, &err) From 914073847de0fb4440dd6489e7d194b42773ffbd Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sun, 11 Jun 2023 16:53:27 +0330 Subject: [PATCH 04/61] add --include-versions flag to skywire-cli log command --- .github/workflows/deploy.yml | 4 +- Makefile | 4 +- cmd/skywire-cli/commands/config/gen_test.go | 77 ++++ cmd/skywire-cli/commands/log/root.go | 25 +- cmd/skywire-cli/commands/visor/info.go | 2 +- go.mod | 1 + go.sum | 2 + .../hashicorp/go-version/CHANGELOG.md | 45 ++ .../github.com/hashicorp/go-version/LICENSE | 354 +++++++++++++++ .../github.com/hashicorp/go-version/README.md | 66 +++ .../hashicorp/go-version/constraint.go | 296 +++++++++++++ .../hashicorp/go-version/version.go | 407 ++++++++++++++++++ .../go-version/version_collection.go | 17 + vendor/modules.txt | 3 + 14 files changed, 1297 insertions(+), 6 deletions(-) create mode 100644 cmd/skywire-cli/commands/config/gen_test.go create mode 100644 vendor/github.com/hashicorp/go-version/CHANGELOG.md create mode 100644 vendor/github.com/hashicorp/go-version/LICENSE create mode 100644 vendor/github.com/hashicorp/go-version/README.md create mode 100644 vendor/github.com/hashicorp/go-version/constraint.go create mode 100644 vendor/github.com/hashicorp/go-version/version.go create mode 100644 vendor/github.com/hashicorp/go-version/version_collection.go diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d3b742f31..8a4566160 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,8 +16,8 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v2 with: - username: ${{ secrets.USERNAME }} - password: ${{ secrets.TOKEN }} + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - uses: actions/checkout@v3 - name: deploy to docker run: | diff --git a/Makefile b/Makefile index ff08311f1..ace5669e0 100644 --- a/Makefile +++ b/Makefile @@ -150,12 +150,12 @@ lint-windows: ## Run linters. Use make install-linters-windows first test: ## Run tests -go clean -testcache &>/dev/null - ${OPTS} go test ${TEST_OPTS} ./internal/... ./pkg/... + ${OPTS} go test ${TEST_OPTS} ./internal/... ./pkg/... ./cmd/... ${OPTS} go test ${TEST_OPTS} test-windows: ## Run tests on windows @go clean -testcache - ${OPTS} go test ${TEST_OPTS} ./internal/... ./pkg/... + ${OPTS} go test ${TEST_OPTS} ./internal/... ./pkg/... ./cmd/... install-linters: ## Install linters - VERSION=latest ./ci_scripts/install-golangci-lint.sh diff --git a/cmd/skywire-cli/commands/config/gen_test.go b/cmd/skywire-cli/commands/config/gen_test.go new file mode 100644 index 000000000..aa001b01e --- /dev/null +++ b/cmd/skywire-cli/commands/config/gen_test.go @@ -0,0 +1,77 @@ +package cliconfig + +import ( + "os" + "os/exec" + "runtime" + "testing" + + "github.com/bitfield/script" +) + +var ( + shell string +) + +func init() { + switch runtime.GOOS { + case "windows": + if _, err := exec.LookPath("bash"); err == nil { + shell = "bash" + } else if _, err := exec.LookPath("powershell"); err == nil { + shell = "powershell" + } else { + panic("Required binaries 'bash' and 'powershell' not found") + } + case "linux", "darwin": + if _, err := exec.LookPath("bash"); err != nil { + panic("Required binary 'bash' not found") + } + shell = "bash" + default: + panic("Unsupported operating system: " + runtime.GOOS) + } +} + +// Reference Issue https://github.com/skycoin/skywire/issues/1606 + +func TestConfigGenCmdFunc(t *testing.T) { + tests := []struct { + name string + command string + expectedErr bool + }{ + { + name: "first config gen -r", + command: "config gen -r -o test-config.json", + expectedErr: false, + }, + { + name: "second config gen -r", + command: "config gen -r -o test-config.json", + expectedErr: false, + }, + { + name: "config gen -rf", + command: "config gen -rf -o test-config.json", + expectedErr: true, + }, + } + _ = os.Remove("test-config.json") //nolint + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := script.Exec(shell + ` -c "go run ../../skywire-cli.go ` + test.command + `"`).Stdout() + if err != nil { + if !test.expectedErr { + t.Fatalf("Expected error: %v, but got: %v", test.expectedErr, err) + } + } + if err == nil { + if test.expectedErr { + t.Fatalf("Expected error: %v, but got: %v", test.expectedErr, err) + } + } + }) + } + _ = os.Remove("test-config.json") //nolint +} diff --git a/cmd/skywire-cli/commands/log/root.go b/cmd/skywire-cli/commands/log/root.go index 62abbf6b6..b984d3ffa 100644 --- a/cmd/skywire-cli/commands/log/root.go +++ b/cmd/skywire-cli/commands/log/root.go @@ -11,9 +11,11 @@ import ( "math/rand" "net/http" "os" + "strings" "sync" "time" + "github.com/hashicorp/go-version" "github.com/skycoin/dmsg/pkg/dmsgget" "github.com/skycoin/dmsg/pkg/dmsghttp" "github.com/spf13/cobra" @@ -37,6 +39,7 @@ var ( logOnly bool surveyOnly bool deleteOnErrors bool + incVer string ) func init() { @@ -46,6 +49,7 @@ func init() { logCmd.Flags().BoolVarP(&surveyOnly, "survey", "v", false, "fetch only surveys") logCmd.Flags().BoolVarP(&deleteOnErrors, "clean", "c", false, "delete files and folders on errors") logCmd.Flags().StringVar(&minv, "minv", "v1.3.4", "minimum version for get logs, default is 1.3.4") + logCmd.Flags().StringVar(&incVer, "include-versions", "", "list of version that not satisfy our minimum version condition, but we want include them") logCmd.Flags().IntVarP(&duration, "duration", "n", 1, "numberof days before today to fetch transport logs for") logCmd.Flags().BoolVar(&allVisors, "all", false, "consider all visors ; no version filtering") logCmd.Flags().IntVar(&batchSize, "batchSize", 50, "number of visor in each batch, default is 50") @@ -128,12 +132,22 @@ var logCmd = &cobra.Command{ dmsgC.EnsureAndObtainSession(ctx, server.PK) //nolint } + minimumVersion, _ := version.NewVersion(minv) //nolint + incVerList := strings.Split(incVer, ",") + start := time.Now() var bulkFolders []string // Get visors data var wg sync.WaitGroup for _, v := range uptimes { - if !allVisors && v.Version < minv { + visorVersion, err := version.NewVersion(v.Version) //nolint + includeV := contains(incVerList, v.Version) + if err != nil && !includeV { + log.Warnf("The version %s for visor %s is not valid", v.Version, v.PubKey) + continue + } + if !allVisors && visorVersion.LessThan(minimumVersion) && !includeV { + log.Warnf("The version %s for visor %s does not satisfy our minimum version condition", v.Version, v.PubKey) continue } wg.Add(1) @@ -259,3 +273,12 @@ func getAllDMSGServers() []dmsgServer { type dmsgServer struct { PK cipher.PubKey `json:"static"` } + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} diff --git a/cmd/skywire-cli/commands/visor/info.go b/cmd/skywire-cli/commands/visor/info.go index bf9bc2fc3..9a5ebca74 100644 --- a/cmd/skywire-cli/commands/visor/info.go +++ b/cmd/skywire-cli/commands/visor/info.go @@ -24,7 +24,7 @@ var pk string func init() { RootCmd.AddCommand(pkCmd) pkCmd.Flags().StringVarP(&path, "input", "i", "", "path of input config file.") - pkCmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from "+fmt.Sprintf("%s", visorconfig.PackageConfig())) //nolint + pkCmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from "+fmt.Sprintf("%v", visorconfig.PackageConfig())) //nolint pkCmd.Flags().BoolVarP(&web, "http", "w", false, "serve public key via http") pkCmd.Flags().StringVarP(&webPort, "prt", "x", "7998", "serve public key via http") RootCmd.AddCommand(summaryCmd) diff --git a/go.mod b/go.mod index 6784dabaa..44f6af3fb 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.8-0.20220103230436-7dbe9a0bd10f github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 + github.com/hashicorp/go-version v1.6.0 github.com/ivanpirog/coloredcobra v1.0.0 github.com/james-barrow/golang-ipc v0.0.0-20210227130457-95e7cc81f5e2 github.com/jaypipes/ghw v0.10.0 diff --git a/go.sum b/go.sum index 66e0b569d..6624d5259 100644 --- a/go.sum +++ b/go.sum @@ -338,6 +338,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= diff --git a/vendor/github.com/hashicorp/go-version/CHANGELOG.md b/vendor/github.com/hashicorp/go-version/CHANGELOG.md new file mode 100644 index 000000000..5f16dd140 --- /dev/null +++ b/vendor/github.com/hashicorp/go-version/CHANGELOG.md @@ -0,0 +1,45 @@ +# 1.6.0 (June 28, 2022) + +FEATURES: + +- Add `Prerelease` function to `Constraint` to return true if the version includes a prerelease field ([#100](https://github.com/hashicorp/go-version/pull/100)) + +# 1.5.0 (May 18, 2022) + +FEATURES: + +- Use `encoding` `TextMarshaler` & `TextUnmarshaler` instead of JSON equivalents ([#95](https://github.com/hashicorp/go-version/pull/95)) +- Add JSON handlers to allow parsing from/to JSON ([#93](https://github.com/hashicorp/go-version/pull/93)) + +# 1.4.0 (January 5, 2022) + +FEATURES: + + - Introduce `MustConstraints()` ([#87](https://github.com/hashicorp/go-version/pull/87)) + - `Constraints`: Introduce `Equals()` and `sort.Interface` methods ([#88](https://github.com/hashicorp/go-version/pull/88)) + +# 1.3.0 (March 31, 2021) + +Please note that CHANGELOG.md does not exist in the source code prior to this release. + +FEATURES: + - Add `Core` function to return a version without prerelease or metadata ([#85](https://github.com/hashicorp/go-version/pull/85)) + +# 1.2.1 (June 17, 2020) + +BUG FIXES: + - Prevent `Version.Equal` method from panicking on `nil` encounter ([#73](https://github.com/hashicorp/go-version/pull/73)) + +# 1.2.0 (April 23, 2019) + +FEATURES: + - Add `GreaterThanOrEqual` and `LessThanOrEqual` helper methods ([#53](https://github.com/hashicorp/go-version/pull/53)) + +# 1.1.0 (Jan 07, 2019) + +FEATURES: + - Add `NewSemver` constructor ([#45](https://github.com/hashicorp/go-version/pull/45)) + +# 1.0.0 (August 24, 2018) + +Initial release. diff --git a/vendor/github.com/hashicorp/go-version/LICENSE b/vendor/github.com/hashicorp/go-version/LICENSE new file mode 100644 index 000000000..c33dcc7c9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-version/LICENSE @@ -0,0 +1,354 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + diff --git a/vendor/github.com/hashicorp/go-version/README.md b/vendor/github.com/hashicorp/go-version/README.md new file mode 100644 index 000000000..4d2505090 --- /dev/null +++ b/vendor/github.com/hashicorp/go-version/README.md @@ -0,0 +1,66 @@ +# Versioning Library for Go +[![Build Status](https://circleci.com/gh/hashicorp/go-version/tree/main.svg?style=svg)](https://circleci.com/gh/hashicorp/go-version/tree/main) +[![GoDoc](https://godoc.org/github.com/hashicorp/go-version?status.svg)](https://godoc.org/github.com/hashicorp/go-version) + +go-version is a library for parsing versions and version constraints, +and verifying versions against a set of constraints. go-version +can sort a collection of versions properly, handles prerelease/beta +versions, can increment versions, etc. + +Versions used with go-version must follow [SemVer](http://semver.org/). + +## Installation and Usage + +Package documentation can be found on +[GoDoc](http://godoc.org/github.com/hashicorp/go-version). + +Installation can be done with a normal `go get`: + +``` +$ go get github.com/hashicorp/go-version +``` + +#### Version Parsing and Comparison + +```go +v1, err := version.NewVersion("1.2") +v2, err := version.NewVersion("1.5+metadata") + +// Comparison example. There is also GreaterThan, Equal, and just +// a simple Compare that returns an int allowing easy >=, <=, etc. +if v1.LessThan(v2) { + fmt.Printf("%s is less than %s", v1, v2) +} +``` + +#### Version Constraints + +```go +v1, err := version.NewVersion("1.2") + +// Constraints example. +constraints, err := version.NewConstraint(">= 1.0, < 1.4") +if constraints.Check(v1) { + fmt.Printf("%s satisfies constraints %s", v1, constraints) +} +``` + +#### Version Sorting + +```go +versionsRaw := []string{"1.1", "0.7.1", "1.4-beta", "1.4", "2"} +versions := make([]*version.Version, len(versionsRaw)) +for i, raw := range versionsRaw { + v, _ := version.NewVersion(raw) + versions[i] = v +} + +// After this, the versions are properly sorted +sort.Sort(version.Collection(versions)) +``` + +## Issues and Contributing + +If you find an issue with this library, please report an issue. If you'd +like, we welcome any contributions. Fork this library and submit a pull +request. diff --git a/vendor/github.com/hashicorp/go-version/constraint.go b/vendor/github.com/hashicorp/go-version/constraint.go new file mode 100644 index 000000000..da5d1aca1 --- /dev/null +++ b/vendor/github.com/hashicorp/go-version/constraint.go @@ -0,0 +1,296 @@ +package version + +import ( + "fmt" + "reflect" + "regexp" + "sort" + "strings" +) + +// Constraint represents a single constraint for a version, such as +// ">= 1.0". +type Constraint struct { + f constraintFunc + op operator + check *Version + original string +} + +func (c *Constraint) Equals(con *Constraint) bool { + return c.op == con.op && c.check.Equal(con.check) +} + +// Constraints is a slice of constraints. We make a custom type so that +// we can add methods to it. +type Constraints []*Constraint + +type constraintFunc func(v, c *Version) bool + +var constraintOperators map[string]constraintOperation + +type constraintOperation struct { + op operator + f constraintFunc +} + +var constraintRegexp *regexp.Regexp + +func init() { + constraintOperators = map[string]constraintOperation{ + "": {op: equal, f: constraintEqual}, + "=": {op: equal, f: constraintEqual}, + "!=": {op: notEqual, f: constraintNotEqual}, + ">": {op: greaterThan, f: constraintGreaterThan}, + "<": {op: lessThan, f: constraintLessThan}, + ">=": {op: greaterThanEqual, f: constraintGreaterThanEqual}, + "<=": {op: lessThanEqual, f: constraintLessThanEqual}, + "~>": {op: pessimistic, f: constraintPessimistic}, + } + + ops := make([]string, 0, len(constraintOperators)) + for k := range constraintOperators { + ops = append(ops, regexp.QuoteMeta(k)) + } + + constraintRegexp = regexp.MustCompile(fmt.Sprintf( + `^\s*(%s)\s*(%s)\s*$`, + strings.Join(ops, "|"), + VersionRegexpRaw)) +} + +// NewConstraint will parse one or more constraints from the given +// constraint string. The string must be a comma-separated list of +// constraints. +func NewConstraint(v string) (Constraints, error) { + vs := strings.Split(v, ",") + result := make([]*Constraint, len(vs)) + for i, single := range vs { + c, err := parseSingle(single) + if err != nil { + return nil, err + } + + result[i] = c + } + + return Constraints(result), nil +} + +// MustConstraints is a helper that wraps a call to a function +// returning (Constraints, error) and panics if error is non-nil. +func MustConstraints(c Constraints, err error) Constraints { + if err != nil { + panic(err) + } + + return c +} + +// Check tests if a version satisfies all the constraints. +func (cs Constraints) Check(v *Version) bool { + for _, c := range cs { + if !c.Check(v) { + return false + } + } + + return true +} + +// Equals compares Constraints with other Constraints +// for equality. This may not represent logical equivalence +// of compared constraints. +// e.g. even though '>0.1,>0.2' is logically equivalent +// to '>0.2' it is *NOT* treated as equal. +// +// Missing operator is treated as equal to '=', whitespaces +// are ignored and constraints are sorted before comaparison. +func (cs Constraints) Equals(c Constraints) bool { + if len(cs) != len(c) { + return false + } + + // make copies to retain order of the original slices + left := make(Constraints, len(cs)) + copy(left, cs) + sort.Stable(left) + right := make(Constraints, len(c)) + copy(right, c) + sort.Stable(right) + + // compare sorted slices + for i, con := range left { + if !con.Equals(right[i]) { + return false + } + } + + return true +} + +func (cs Constraints) Len() int { + return len(cs) +} + +func (cs Constraints) Less(i, j int) bool { + if cs[i].op < cs[j].op { + return true + } + if cs[i].op > cs[j].op { + return false + } + + return cs[i].check.LessThan(cs[j].check) +} + +func (cs Constraints) Swap(i, j int) { + cs[i], cs[j] = cs[j], cs[i] +} + +// Returns the string format of the constraints +func (cs Constraints) String() string { + csStr := make([]string, len(cs)) + for i, c := range cs { + csStr[i] = c.String() + } + + return strings.Join(csStr, ",") +} + +// Check tests if a constraint is validated by the given version. +func (c *Constraint) Check(v *Version) bool { + return c.f(v, c.check) +} + +// Prerelease returns true if the version underlying this constraint +// contains a prerelease field. +func (c *Constraint) Prerelease() bool { + return len(c.check.Prerelease()) > 0 +} + +func (c *Constraint) String() string { + return c.original +} + +func parseSingle(v string) (*Constraint, error) { + matches := constraintRegexp.FindStringSubmatch(v) + if matches == nil { + return nil, fmt.Errorf("Malformed constraint: %s", v) + } + + check, err := NewVersion(matches[2]) + if err != nil { + return nil, err + } + + cop := constraintOperators[matches[1]] + + return &Constraint{ + f: cop.f, + op: cop.op, + check: check, + original: v, + }, nil +} + +func prereleaseCheck(v, c *Version) bool { + switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; { + case cPre && vPre: + // A constraint with a pre-release can only match a pre-release version + // with the same base segments. + return reflect.DeepEqual(c.Segments64(), v.Segments64()) + + case !cPre && vPre: + // A constraint without a pre-release can only match a version without a + // pre-release. + return false + + case cPre && !vPre: + // OK, except with the pessimistic operator + case !cPre && !vPre: + // OK + } + return true +} + +//------------------------------------------------------------------- +// Constraint functions +//------------------------------------------------------------------- + +type operator rune + +const ( + equal operator = '=' + notEqual operator = '≠' + greaterThan operator = '>' + lessThan operator = '<' + greaterThanEqual operator = '≥' + lessThanEqual operator = '≤' + pessimistic operator = '~' +) + +func constraintEqual(v, c *Version) bool { + return v.Equal(c) +} + +func constraintNotEqual(v, c *Version) bool { + return !v.Equal(c) +} + +func constraintGreaterThan(v, c *Version) bool { + return prereleaseCheck(v, c) && v.Compare(c) == 1 +} + +func constraintLessThan(v, c *Version) bool { + return prereleaseCheck(v, c) && v.Compare(c) == -1 +} + +func constraintGreaterThanEqual(v, c *Version) bool { + return prereleaseCheck(v, c) && v.Compare(c) >= 0 +} + +func constraintLessThanEqual(v, c *Version) bool { + return prereleaseCheck(v, c) && v.Compare(c) <= 0 +} + +func constraintPessimistic(v, c *Version) bool { + // Using a pessimistic constraint with a pre-release, restricts versions to pre-releases + if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") { + return false + } + + // If the version being checked is naturally less than the constraint, then there + // is no way for the version to be valid against the constraint + if v.LessThan(c) { + return false + } + // We'll use this more than once, so grab the length now so it's a little cleaner + // to write the later checks + cs := len(c.segments) + + // If the version being checked has less specificity than the constraint, then there + // is no way for the version to be valid against the constraint + if cs > len(v.segments) { + return false + } + + // Check the segments in the constraint against those in the version. If the version + // being checked, at any point, does not have the same values in each index of the + // constraints segments, then it cannot be valid against the constraint. + for i := 0; i < c.si-1; i++ { + if v.segments[i] != c.segments[i] { + return false + } + } + + // Check the last part of the segment in the constraint. If the version segment at + // this index is less than the constraints segment at this index, then it cannot + // be valid against the constraint + if c.segments[cs-1] > v.segments[cs-1] { + return false + } + + // If nothing has rejected the version by now, it's valid + return true +} diff --git a/vendor/github.com/hashicorp/go-version/version.go b/vendor/github.com/hashicorp/go-version/version.go new file mode 100644 index 000000000..e87df6990 --- /dev/null +++ b/vendor/github.com/hashicorp/go-version/version.go @@ -0,0 +1,407 @@ +package version + +import ( + "bytes" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" +) + +// The compiled regular expression used to test the validity of a version. +var ( + versionRegexp *regexp.Regexp + semverRegexp *regexp.Regexp +) + +// The raw regular expression string used for testing the validity +// of a version. +const ( + VersionRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` + + `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-?([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` + + `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` + + `?` + + // SemverRegexpRaw requires a separator between version and prerelease + SemverRegexpRaw string = `v?([0-9]+(\.[0-9]+)*?)` + + `(-([0-9]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)|(-([A-Za-z\-~]+[0-9A-Za-z\-~]*(\.[0-9A-Za-z\-~]+)*)))?` + + `(\+([0-9A-Za-z\-~]+(\.[0-9A-Za-z\-~]+)*))?` + + `?` +) + +// Version represents a single version. +type Version struct { + metadata string + pre string + segments []int64 + si int + original string +} + +func init() { + versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$") + semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$") +} + +// NewVersion parses the given version and returns a new +// Version. +func NewVersion(v string) (*Version, error) { + return newVersion(v, versionRegexp) +} + +// NewSemver parses the given version and returns a new +// Version that adheres strictly to SemVer specs +// https://semver.org/ +func NewSemver(v string) (*Version, error) { + return newVersion(v, semverRegexp) +} + +func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { + matches := pattern.FindStringSubmatch(v) + if matches == nil { + return nil, fmt.Errorf("Malformed version: %s", v) + } + segmentsStr := strings.Split(matches[1], ".") + segments := make([]int64, len(segmentsStr)) + for i, str := range segmentsStr { + val, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return nil, fmt.Errorf( + "Error parsing version: %s", err) + } + + segments[i] = val + } + + // Even though we could support more than three segments, if we + // got less than three, pad it with 0s. This is to cover the basic + // default usecase of semver, which is MAJOR.MINOR.PATCH at the minimum + for i := len(segments); i < 3; i++ { + segments = append(segments, 0) + } + + pre := matches[7] + if pre == "" { + pre = matches[4] + } + + return &Version{ + metadata: matches[10], + pre: pre, + segments: segments, + si: len(segmentsStr), + original: v, + }, nil +} + +// Must is a helper that wraps a call to a function returning (*Version, error) +// and panics if error is non-nil. +func Must(v *Version, err error) *Version { + if err != nil { + panic(err) + } + + return v +} + +// Compare compares this version to another version. This +// returns -1, 0, or 1 if this version is smaller, equal, +// or larger than the other version, respectively. +// +// If you want boolean results, use the LessThan, Equal, +// GreaterThan, GreaterThanOrEqual or LessThanOrEqual methods. +func (v *Version) Compare(other *Version) int { + // A quick, efficient equality check + if v.String() == other.String() { + return 0 + } + + segmentsSelf := v.Segments64() + segmentsOther := other.Segments64() + + // If the segments are the same, we must compare on prerelease info + if reflect.DeepEqual(segmentsSelf, segmentsOther) { + preSelf := v.Prerelease() + preOther := other.Prerelease() + if preSelf == "" && preOther == "" { + return 0 + } + if preSelf == "" { + return 1 + } + if preOther == "" { + return -1 + } + + return comparePrereleases(preSelf, preOther) + } + + // Get the highest specificity (hS), or if they're equal, just use segmentSelf length + lenSelf := len(segmentsSelf) + lenOther := len(segmentsOther) + hS := lenSelf + if lenSelf < lenOther { + hS = lenOther + } + // Compare the segments + // Because a constraint could have more/less specificity than the version it's + // checking, we need to account for a lopsided or jagged comparison + for i := 0; i < hS; i++ { + if i > lenSelf-1 { + // This means Self had the lower specificity + // Check to see if the remaining segments in Other are all zeros + if !allZero(segmentsOther[i:]) { + // if not, it means that Other has to be greater than Self + return -1 + } + break + } else if i > lenOther-1 { + // this means Other had the lower specificity + // Check to see if the remaining segments in Self are all zeros - + if !allZero(segmentsSelf[i:]) { + //if not, it means that Self has to be greater than Other + return 1 + } + break + } + lhs := segmentsSelf[i] + rhs := segmentsOther[i] + if lhs == rhs { + continue + } else if lhs < rhs { + return -1 + } + // Otherwis, rhs was > lhs, they're not equal + return 1 + } + + // if we got this far, they're equal + return 0 +} + +func allZero(segs []int64) bool { + for _, s := range segs { + if s != 0 { + return false + } + } + return true +} + +func comparePart(preSelf string, preOther string) int { + if preSelf == preOther { + return 0 + } + + var selfInt int64 + selfNumeric := true + selfInt, err := strconv.ParseInt(preSelf, 10, 64) + if err != nil { + selfNumeric = false + } + + var otherInt int64 + otherNumeric := true + otherInt, err = strconv.ParseInt(preOther, 10, 64) + if err != nil { + otherNumeric = false + } + + // if a part is empty, we use the other to decide + if preSelf == "" { + if otherNumeric { + return -1 + } + return 1 + } + + if preOther == "" { + if selfNumeric { + return 1 + } + return -1 + } + + if selfNumeric && !otherNumeric { + return -1 + } else if !selfNumeric && otherNumeric { + return 1 + } else if !selfNumeric && !otherNumeric && preSelf > preOther { + return 1 + } else if selfInt > otherInt { + return 1 + } + + return -1 +} + +func comparePrereleases(v string, other string) int { + // the same pre release! + if v == other { + return 0 + } + + // split both pre releases for analyse their parts + selfPreReleaseMeta := strings.Split(v, ".") + otherPreReleaseMeta := strings.Split(other, ".") + + selfPreReleaseLen := len(selfPreReleaseMeta) + otherPreReleaseLen := len(otherPreReleaseMeta) + + biggestLen := otherPreReleaseLen + if selfPreReleaseLen > otherPreReleaseLen { + biggestLen = selfPreReleaseLen + } + + // loop for parts to find the first difference + for i := 0; i < biggestLen; i = i + 1 { + partSelfPre := "" + if i < selfPreReleaseLen { + partSelfPre = selfPreReleaseMeta[i] + } + + partOtherPre := "" + if i < otherPreReleaseLen { + partOtherPre = otherPreReleaseMeta[i] + } + + compare := comparePart(partSelfPre, partOtherPre) + // if parts are equals, continue the loop + if compare != 0 { + return compare + } + } + + return 0 +} + +// Core returns a new version constructed from only the MAJOR.MINOR.PATCH +// segments of the version, without prerelease or metadata. +func (v *Version) Core() *Version { + segments := v.Segments64() + segmentsOnly := fmt.Sprintf("%d.%d.%d", segments[0], segments[1], segments[2]) + return Must(NewVersion(segmentsOnly)) +} + +// Equal tests if two versions are equal. +func (v *Version) Equal(o *Version) bool { + if v == nil || o == nil { + return v == o + } + + return v.Compare(o) == 0 +} + +// GreaterThan tests if this version is greater than another version. +func (v *Version) GreaterThan(o *Version) bool { + return v.Compare(o) > 0 +} + +// GreaterThanOrEqual tests if this version is greater than or equal to another version. +func (v *Version) GreaterThanOrEqual(o *Version) bool { + return v.Compare(o) >= 0 +} + +// LessThan tests if this version is less than another version. +func (v *Version) LessThan(o *Version) bool { + return v.Compare(o) < 0 +} + +// LessThanOrEqual tests if this version is less than or equal to another version. +func (v *Version) LessThanOrEqual(o *Version) bool { + return v.Compare(o) <= 0 +} + +// Metadata returns any metadata that was part of the version +// string. +// +// Metadata is anything that comes after the "+" in the version. +// For example, with "1.2.3+beta", the metadata is "beta". +func (v *Version) Metadata() string { + return v.metadata +} + +// Prerelease returns any prerelease data that is part of the version, +// or blank if there is no prerelease data. +// +// Prerelease information is anything that comes after the "-" in the +// version (but before any metadata). For example, with "1.2.3-beta", +// the prerelease information is "beta". +func (v *Version) Prerelease() string { + return v.pre +} + +// Segments returns the numeric segments of the version as a slice of ints. +// +// This excludes any metadata or pre-release information. For example, +// for a version "1.2.3-beta", segments will return a slice of +// 1, 2, 3. +func (v *Version) Segments() []int { + segmentSlice := make([]int, len(v.segments)) + for i, v := range v.segments { + segmentSlice[i] = int(v) + } + return segmentSlice +} + +// Segments64 returns the numeric segments of the version as a slice of int64s. +// +// This excludes any metadata or pre-release information. For example, +// for a version "1.2.3-beta", segments will return a slice of +// 1, 2, 3. +func (v *Version) Segments64() []int64 { + result := make([]int64, len(v.segments)) + copy(result, v.segments) + return result +} + +// String returns the full version string included pre-release +// and metadata information. +// +// This value is rebuilt according to the parsed segments and other +// information. Therefore, ambiguities in the version string such as +// prefixed zeroes (1.04.0 => 1.4.0), `v` prefix (v1.0.0 => 1.0.0), and +// missing parts (1.0 => 1.0.0) will be made into a canonicalized form +// as shown in the parenthesized examples. +func (v *Version) String() string { + var buf bytes.Buffer + fmtParts := make([]string, len(v.segments)) + for i, s := range v.segments { + // We can ignore err here since we've pre-parsed the values in segments + str := strconv.FormatInt(s, 10) + fmtParts[i] = str + } + fmt.Fprintf(&buf, strings.Join(fmtParts, ".")) + if v.pre != "" { + fmt.Fprintf(&buf, "-%s", v.pre) + } + if v.metadata != "" { + fmt.Fprintf(&buf, "+%s", v.metadata) + } + + return buf.String() +} + +// Original returns the original parsed version as-is, including any +// potential whitespace, `v` prefix, etc. +func (v *Version) Original() string { + return v.original +} + +// UnmarshalText implements encoding.TextUnmarshaler interface. +func (v *Version) UnmarshalText(b []byte) error { + temp, err := NewVersion(string(b)) + if err != nil { + return err + } + + *v = *temp + + return nil +} + +// MarshalText implements encoding.TextMarshaler interface. +func (v *Version) MarshalText() ([]byte, error) { + return []byte(v.String()), nil +} diff --git a/vendor/github.com/hashicorp/go-version/version_collection.go b/vendor/github.com/hashicorp/go-version/version_collection.go new file mode 100644 index 000000000..cc888d43e --- /dev/null +++ b/vendor/github.com/hashicorp/go-version/version_collection.go @@ -0,0 +1,17 @@ +package version + +// Collection is a type that implements the sort.Interface interface +// so that versions can be sorted. +type Collection []*Version + +func (v Collection) Len() int { + return len(v) +} + +func (v Collection) Less(i, j int) bool { + return v[i].LessThan(v[j]) +} + +func (v Collection) Swap(i, j int) { + v[i], v[j] = v[j], v[i] +} diff --git a/vendor/modules.txt b/vendor/modules.txt index db796bda7..6afe8541c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -150,6 +150,9 @@ github.com/gopherjs/gopherjs/js # github.com/gorilla/securecookie v1.1.1 ## explicit github.com/gorilla/securecookie +# github.com/hashicorp/go-version v1.6.0 +## explicit +github.com/hashicorp/go-version # github.com/inconshreveable/mousetrap v1.0.0 ## explicit github.com/inconshreveable/mousetrap From 458c156d729adbd89333d36cdc8c58d6b070b9e2 Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Mon, 12 Jun 2023 13:06:57 -0400 Subject: [PATCH 05/61] As confirmation for Skychat access --- .../skychat-settings.component.ts | 21 +++++++++++++++++++ .../src/assets/i18n/en.json | 3 ++- .../src/assets/i18n/es.json | 3 ++- .../src/assets/i18n/es_base.json | 3 ++- 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts index 49805c784..a2bb03924 100644 --- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts +++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component.ts @@ -11,6 +11,7 @@ import { processServiceError } from 'src/app/utils/errors'; import { OperationError } from 'src/app/utils/operation-error'; import { AppsService } from 'src/app/services/apps.service'; import { Application } from 'src/app/app.datatypes'; +import GeneralUtils from 'src/app/utils/generalUtils'; /** * Modal window used for configuring the Skychat app. @@ -24,6 +25,7 @@ export class SkychatSettingsComponent implements OnInit, OnDestroy { @ViewChild('button') button: ButtonComponent; form: UntypedFormGroup; + private formSubscription: Subscription; private operationSubscription: Subscription; /** @@ -44,6 +46,7 @@ export class SkychatSettingsComponent implements OnInit, OnDestroy { private formBuilder: UntypedFormBuilder, public dialogRef: MatDialogRef, private snackbarService: SnackbarService, + private dialog: MatDialog, ) { } ngOnInit() { @@ -52,6 +55,20 @@ export class SkychatSettingsComponent implements OnInit, OnDestroy { port: ['', Validators.compose([Validators.required, Validators.min(1025), Validators.max(65536)])], }); + this.formSubscription = this.form.get('localhostOnly').valueChanges.subscribe(value => { + // If "no" is selected ask for confirmation. + if (!value) { + this.form.get('localhostOnly').setValue(true); + const confirmationDialog = GeneralUtils.createConfirmationDialog(this.dialog, 'apps.skychat-settings.non-localhost-confirmation'); + + confirmationDialog.componentInstance.operationAccepted.subscribe(() => { + confirmationDialog.componentInstance.closeModal(); + + this.form.get('localhostOnly').setValue(false, { emitEvent: false }); + }); + } + }); + // Get the current values saved on the visor, if returned by the API. if (this.data.args && this.data.args.length > 0) { for (let i = 0; i < this.data.args.length; i++) { @@ -68,6 +85,10 @@ export class SkychatSettingsComponent implements OnInit, OnDestroy { } ngOnDestroy() { + if (this.formSubscription) { + this.formSubscription.unsubscribe(); + } + if (this.operationSubscription) { this.operationSubscription.unsubscribe(); } diff --git a/static/skywire-manager-src/src/assets/i18n/en.json b/static/skywire-manager-src/src/assets/i18n/en.json index 9f71be51a..b01a27b8e 100644 --- a/static/skywire-manager-src/src/assets/i18n/en.json +++ b/static/skywire-manager-src/src/assets/i18n/en.json @@ -463,7 +463,8 @@ "port": "Port", "save": "Save", "changes-made": "The changes have been made.", - "port-error": "Must be a valid number between 1025 and 65536." + "port-error": "Must be a valid number between 1025 and 65536.", + "non-localhost-confirmation": "This will allow to use the app from anywhere on the internet. Are you sure you vant to continue?" }, "vpn-socks-server-settings": { "socks-title": "Skysocks Settings", diff --git a/static/skywire-manager-src/src/assets/i18n/es.json b/static/skywire-manager-src/src/assets/i18n/es.json index ad795e30e..f369ed1fe 100644 --- a/static/skywire-manager-src/src/assets/i18n/es.json +++ b/static/skywire-manager-src/src/assets/i18n/es.json @@ -467,7 +467,8 @@ "port": "Puerto", "save": "Guardar", "changes-made": "Los cambios han sido realizados.", - "port-error": "Debe ser un número válido entre 1025 y 65536." + "port-error": "Debe ser un número válido entre 1025 y 65536.", + "non-localhost-confirmation": "Esto permitirá usar la aplicación desde cualquier lugar en Internet. ¿Seguro que desea continuar?" }, "vpn-socks-server-settings": { "socks-title": "Configuración de Skysocks", diff --git a/static/skywire-manager-src/src/assets/i18n/es_base.json b/static/skywire-manager-src/src/assets/i18n/es_base.json index 505872da8..7965323a2 100644 --- a/static/skywire-manager-src/src/assets/i18n/es_base.json +++ b/static/skywire-manager-src/src/assets/i18n/es_base.json @@ -467,7 +467,8 @@ "port": "Port", "save": "Save", "changes-made": "The changes have been made.", - "port-error": "Must be a valid number between 1025 and 65536." + "port-error": "Must be a valid number between 1025 and 65536.", + "non-localhost-confirmation": "This will allow to use the app from anywhere on the internet. Are you sure you vant to continue?" }, "vpn-socks-server-settings": { "socks-title": "Skysocks Settings", From 3c685e3492d8befb14f253f9f3754f0f95fea796 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 21 Jun 2023 12:08:33 +0330 Subject: [PATCH 06/61] add new skywire-cli command as dmsghttp update --- cmd/skywire-cli/commands/dmsghttp/root.go | 102 ++++++++++++++++++++++ cmd/skywire-cli/commands/root.go | 2 + 2 files changed, 104 insertions(+) create mode 100644 cmd/skywire-cli/commands/dmsghttp/root.go diff --git a/cmd/skywire-cli/commands/dmsghttp/root.go b/cmd/skywire-cli/commands/dmsghttp/root.go new file mode 100644 index 000000000..b27373057 --- /dev/null +++ b/cmd/skywire-cli/commands/dmsghttp/root.go @@ -0,0 +1,102 @@ +// Package clidmsghttp cmd/skywire-cli/commands/dmsghttp/root.go +package clidmsghttp + +import ( + "context" + "encoding/json" + "io" + "io/ioutil" + "net/http" + "os" + + "github.com/spf13/cobra" + + "github.com/skycoin/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire-utilities/pkg/httputil" + "github.com/skycoin/skywire-utilities/pkg/logging" + "github.com/skycoin/skywire-utilities/pkg/skyenv" +) + +var ( + path string +) + +func init() { + dmsghttpCmd.Flags().SortFlags = false + dmsghttpCmd.Flags().StringVarP(&path, "path", "p", "/opt/skywire/dmsghttp-config.json", "path of dmsghttp-config file, default is for pkg installation") +} + +// RootCmd is surveyCmd +var RootCmd = dmsghttpCmd + +var dmsghttpCmd = &cobra.Command{ + Use: "dmsghttp update", + Short: "update dmsghttp-config.json file from config bootstrap service", + Run: func(cmd *cobra.Command, args []string) { + log := logging.MustGetLogger("dmsghttp_updater") + + ctx, cancel := cmdutil.SignalContext(context.Background(), log) + defer cancel() + go func() { + <-ctx.Done() + cancel() + os.Exit(1) + }() + + dmsghttpConf, err := fetchDmsghttpConf() + if err != nil { + log.WithError(err).Error("Cannot fetching updated dmsghttp-config data") + } + + file, err := json.MarshalIndent(dmsghttpConf, "", " ") + if err != nil { + log.WithError(err).Error("Error accurs during marshal content to json file") + } + + err = ioutil.WriteFile(path, file, 0600) + if err != nil { + log.WithError(err).Errorf("Cannot save new dmsghttp-config.json file at %s", path) + } + }, +} + +type dmsghttpConf struct { //nolint + Test httputil.DMSGHTTPConf `json:"test"` + Prod httputil.DMSGHTTPConf `json:"prod"` +} + +func fetchDmsghttpConf() (dmsghttpConf, error) { + var newConf dmsghttpConf + var prodConf httputil.DMSGHTTPConf + prodResp, err := http.Get(skyenv.ServiceConfAddr + "/dmsghttp") + if err != nil { + return newConf, err + } + defer prodResp.Body.Close() //nolint + body, err := io.ReadAll(prodResp.Body) + if err != nil { + return newConf, err + } + err = json.Unmarshal(body, &prodConf) + if err != nil { + return newConf, err + } + newConf.Prod = prodConf + + var testConf httputil.DMSGHTTPConf + testResp, err := http.Get(skyenv.TestServiceConfAddr + "/dmsghttp") + if err != nil { + return newConf, err + } + defer testResp.Body.Close() //nolint + body, err = io.ReadAll(testResp.Body) + if err != nil { + return newConf, err + } + err = json.Unmarshal(body, &testConf) + if err != nil { + return newConf, err + } + newConf.Test = testConf + return newConf, nil +} diff --git a/cmd/skywire-cli/commands/root.go b/cmd/skywire-cli/commands/root.go index 417f273f0..b474a1276 100644 --- a/cmd/skywire-cli/commands/root.go +++ b/cmd/skywire-cli/commands/root.go @@ -15,6 +15,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" clicompletion "github.com/skycoin/skywire/cmd/skywire-cli/commands/completion" cliconfig "github.com/skycoin/skywire/cmd/skywire-cli/commands/config" + clidmsghttp "github.com/skycoin/skywire/cmd/skywire-cli/commands/dmsghttp" clidmsgpty "github.com/skycoin/skywire/cmd/skywire-cli/commands/dmsgpty" clilog "github.com/skycoin/skywire/cmd/skywire-cli/commands/log" climdisc "github.com/skycoin/skywire/cmd/skywire-cli/commands/mdisc" @@ -195,6 +196,7 @@ func init() { cliskysocksc.RootCmd, treeCmd, docCmd, + clidmsghttp.RootCmd, ) var jsonOutput bool RootCmd.PersistentFlags().BoolVar(&jsonOutput, internal.JSONString, false, "print output in json") From f74a6e60024a2e3749e3f687af57204979d76364 Mon Sep 17 00:00:00 2001 From: Senyoret1 <34079003+Senyoret1@users.noreply.github.com> Date: Thu, 13 Jul 2023 16:42:54 -0400 Subject: [PATCH 07/61] UI improvements --- .../skywire-manager-src/src/app/app.module.ts | 2 + .../tab-selector/tab-selector.component.html | 9 ++++ .../tab-selector/tab-selector.component.scss | 17 ++++++++ .../tab-selector/tab-selector.component.ts | 43 +++++++++++++++++++ .../node-logs/node-logs.component.html | 6 +-- .../node-logs/node-logs.component.scss | 22 ++-------- .../node-apps-list/log/log.component.html | 13 ++++-- .../node-apps-list/log/log.component.scss | 20 +++------ .../skysocks-client-settings.component.html | 15 ++++--- .../skysocks-client-settings.component.scss | 18 +++++++- .../skysocks-client-settings.component.ts | 33 ++++++++++++++ .../src/assets/scss/_dialogs.scss | 24 +++++++++++ 12 files changed, 175 insertions(+), 47 deletions(-) create mode 100644 static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.html create mode 100644 static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.scss create mode 100644 static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.ts diff --git a/static/skywire-manager-src/src/app/app.module.ts b/static/skywire-manager-src/src/app/app.module.ts index 36fc08274..f03105cd2 100644 --- a/static/skywire-manager-src/src/app/app.module.ts +++ b/static/skywire-manager-src/src/app/app.module.ts @@ -101,6 +101,7 @@ import { RewardsAddressComponent } from './components/pages/node/node-info/node- import { BulkRewardAddressChangerComponent } from './components/layout/bulk-reward-address-changer/bulk-reward-address-changer.component'; import { UserAppSettingsComponent } from './components/pages/node/apps/node-apps/user-app-settings/user-app-settings.component'; import { NodeLogsComponent } from './components/pages/node/actions/node-logs/node-logs.component'; +import { TabSelectorComponent } from './components/layout/tab-selector/tab-selector.component'; const globalRippleConfig: RippleGlobalOptions = { disabled: true, @@ -178,6 +179,7 @@ const globalRippleConfig: RippleGlobalOptions = { BulkRewardAddressChangerComponent, UserAppSettingsComponent, NodeLogsComponent, + TabSelectorComponent, ], imports: [ BrowserModule, diff --git a/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.html b/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.html new file mode 100644 index 000000000..a1a4b1348 --- /dev/null +++ b/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.html @@ -0,0 +1,9 @@ +
+
+
+ {{ tabNames[selectedTab] | translate }} +
+ expand_more +
+
+
diff --git a/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.scss b/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.scss new file mode 100644 index 000000000..93db0b5af --- /dev/null +++ b/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.scss @@ -0,0 +1,17 @@ +.top-dialog-button{ + .top-dialog-button-content { + display: flex; + + .tab-name { + flex-grow: 1; + margin-right: 10px; + align-self: center; + } + + .icon { + font-size: 20px; + flex-shrink: 0; + align-self: center; + } + } +} diff --git a/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.ts b/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.ts new file mode 100644 index 000000000..b029719a9 --- /dev/null +++ b/static/skywire-manager-src/src/app/components/layout/tab-selector/tab-selector.component.ts @@ -0,0 +1,43 @@ +import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; +import { SelectOptionComponent, SelectableOption } from '../select-option/select-option.component'; +import { MatDialog } from '@angular/material/dialog'; + +/** + * Button for changing the selected tab of an app-tab-selector component in small screens. + */ +@Component({ + selector: 'app-tab-selector', + templateUrl: './tab-selector.component.html', + styleUrls: ['./tab-selector.component.scss'] +}) +export class TabSelectorComponent implements OnDestroy { + // Name of the available tabs, for the translation pipe. + @Input() tabNames: string[] = ['']; + // Index of the currently selected tab. + @Input() selectedTab = 0; + // Event emited if the user selects a different tab. The selectedTab var is not + // updated automatically when this event is sent. + @Output() tabChanged = new EventEmitter(); + + constructor( + private dialog: MatDialog + ) { } + + ngOnDestroy() { + this.tabChanged.complete(); + } + + showTabSelector() { + const options: SelectableOption[] = []; + + // Create a list with all the tabs. + this.tabNames.forEach((name, i) => { + options.push({ icon: i === this.selectedTab ? 'check' : '', label: name }) + }); + + // Show the tab selection modal window. + SelectOptionComponent.openDialog(this.dialog, options, 'node.logs.filter-title').afterClosed().subscribe((selectedOption: number) => { + this.tabChanged.emit(selectedOption - 1); + }); + } +} diff --git a/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.html b/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.html index a708bdb66..b5f9818fe 100644 --- a/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.html +++ b/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.html @@ -1,7 +1,7 @@ -
-
+
+
{{ 'node.logs.selected-filter' | translate }} {{ ('node.logs.' + levelDetails.get(currentMinimumLevel).levelFilterName) | translate }} @@ -10,7 +10,7 @@ {{ 'node.logs.filter-ignored' | translate:{number: logEntries.length - filteredLogEntries.length} }}
-
+
diff --git a/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.scss b/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.scss index a74a96c7a..dc607e837 100644 --- a/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.scss +++ b/static/skywire-manager-src/src/app/components/pages/node/actions/node-logs/node-logs.component.scss @@ -1,19 +1,9 @@ @import "variables"; -.filter-area { - margin-left: -$mat-dialog-padding; - margin-right: -$mat-dialog-padding; - font-size: $font-size-smaller; - color: $light-gray; - background-color: #d9deeb; - cursor: pointer; - - &:hover { - background-color: darken(#d9deeb, 3%); - } +.top-dialog-button { + color: $light-gray !important; - .filter-content { - padding: 10px $mat-dialog-padding; + .top-dialog-button-content { text-align: center; .actual-value { @@ -24,12 +14,6 @@ font-size: $font-size-mini-plus; } } - - .filter-margin { - height: 1px; - background-color: $modal-separator; - margin: 0 12px; - } } .log-entry { diff --git a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/log/log.component.html b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/log/log.component.html index 6ef147466..82aa52d4d 100644 --- a/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/log/log.component.html +++ b/static/skywire-manager-src/src/app/components/pages/node/apps/node-apps-list/log/log.component.html @@ -1,10 +1,15 @@ -