Compare commits

...

396 Commits

Author SHA1 Message Date
540c9cb647 HACK: prebuild storagenode gui 2023-10-05 09:27:20 -06:00
Andrii Kotko
a331610a8a
release v1.89.5 2023-10-05 15:45:40 +03:00
Egon Elbre
98e778e26c go.mod: bump uplink
This brings in 32bit arm bug fix.

Change-Id: I61b72abf2366643f58b1e670d2e02f4072aee0b5
2023-10-05 15:44:16 +03:00
Ethan Adams
20232e435a satellite/email: Add support for unauthenticated, cleartext SMTP connections
Change-Id: I11b4852122764c1ede188ca40d5edb14f2c4ee72
2023-10-05 10:33:11 +03:00
paul cannon
a9265ae543 satellite/{admin,console,satellitedb}: fix geofence removal
deleteGeofenceForProject wasn't able to work correctly, because
Console().Projects().Update() declines to update default_placement when
the input value is 0.

This introduces a Console().Projects().UpdateDefaultPlacement() method,
congruent to the method of the same name on Console().Users().
deleteGeofenceForProject now uses this new method, so that specifying a
new placement of 0 will work correctly.

Change-Id: I4589b36707f7e4f1cfdc66543520b0d4205c1a84
2023-10-05 10:33:11 +03:00
Sean Harvey
e98b94f114 satellite/satellitedb: fix DefaultPlacement overwritten on user
this fixes cases where it's possible to update a user and the
DefaultPlacement field gets overwritten to the zero value.

it also adds UpdateDefaultPlacement which can be used to set
DefaultPlacement directly. This is needed for the geofencing
endpoints in satellite admin to set the DefaultPlacement back
to zero to delete geofencing for a user.

Change-Id: If2c798dabfa6773ed6023fb8257bf00ec7bc2e68
2023-10-04 12:05:04 +03:00
Wilfred Asomani
00cd29cc6d satellite/{web,console}: return empty payments list on no wallet error
This change modifies wallet payments endpoints to return empty lists
instead of returning a 404 error if a wallet is not found for a user.

Change-Id: Ic765fecbc8183d14f179ce1d510ae512d8e0c4a9
2023-10-04 12:05:04 +03:00
Sean Harvey
68fe9a0a52 satellite/metainfo: fix inconsistency with auth check error
This fixes an inconsistency with error returned on copy and move
endpoints to match other endpoints. validateAuth() is already
wrapping the RPC status around the error, so this shouldn't be
doing it again.

This also ensures that rate limit errors for FinishCopyObject and
FinishMoveObject are correctly returned as rpcstatus.ResourceExhausted
so uplink can correctly map these to uplink.ErrTooManyRequests.

Change-Id: I6bf6185b456d6774b99d56cf3d7d8f8aa2afa0e8
2023-10-04 12:05:04 +03:00
Jeremy Wharton
b9c84d637b cmd/satellite: copy Vuetify distribution folder to satellite
This change ensures that files generated by building the Vuetify
project are copied to the satellite container.

References #6251

Change-Id: If56fe754d51f1487a3b3c2cf98c40e3010539121
2023-09-29 19:24:51 +03:00
Wilfred Asomani
822c59638e web/satellite: patch listing token history issue
This change patches an issue where a user who has not claimed a wallet
would see the error "Can not list token payment history" on the all
projects dashboard.

Issue: https://github.com/storj/storj/issues/6358

Change-Id: I0783fae2c4441be4495b9c8bd82cf6dbe6eea557
2023-09-29 19:24:51 +03:00
Ivan Fraixedes
5c5306cef2 satellite/admin/back-office/ui: Delete yarn.lock file
The yarn.lock file came from the former repository where the back office
UI was developed.

It seems that our build process complains about some dirty state related
to this file. Because we don't use Yarn, we delete the file, hoping to
resolve the build issues.

Change-Id: I5febd8292657289d0fc67e08151c6c8b5ac8b5dc
2023-09-29 19:24:51 +03:00
Moby von Briesen
c14e4b1eb4 satellite/console: update CSP
Include *.storjsatelliteshare.io in the `connect-src` portion of the CSP
for the satellite UI.

Change-Id: Ic8c3d0cf892a3275866634cae3e9260d925e1c3e
2023-09-27 15:49:48 +00:00
Márton Elek
58b98bc335 satellite/repair: repair is configurable to work only on included/excluded placements
This patch finishes the placement aware repair.

We already introduced the parameters to select only the jobs for specific placements, the remaining part is just to configure the exclude/include rules. + a full e2e unit test.

Change-Id: I223ba84e8ab7481a53e5a444596c7a5ae51573c5
2023-09-27 14:54:06 +00:00
Vitalii
63645205c0 web/satellite: fix object browser pagination for the folders
Fix pagination for the folders which are not on the first page.
Also, fixed object count calculation inside folders.

Issue:
https://github.com/storj/customer-issues/issues/1055

Change-Id: I1d0fbb8856f13be6fb20698315a7e4d20b4affd9
2023-09-27 14:12:02 +00:00
Michal Niewrzal
e1215d5da8 satellite/overlay: add AOST to GetParticipatingNodes method
This method is sometimes ends with transaction error. Most probably
because it's trying to do full table scan on nodes table which is
heavily used. Adding AOST should help with DB contention.

Change-Id: Ibd4358d28dc26922b60c6b30862f20e7c0662cd1
2023-09-27 12:00:10 +00:00
Ivan Fraixedes
2a8e5aecfd
satellite/admin/back-office/ui: Don't ignore package-lock.json
When the new back office UI sources where copied from former repository
I didn't realize that the .gitignore had the package-lock.json file.

This commit remove the package-lock.json file, so it can be tracked, in
order to have reproducible builds.

The lack of the file caused the build to fail due to `npm ci` requires
it.

Change-Id: Ibe493d0cd5762afe5caabe9b77a333fd6daa5373
2023-09-27 13:17:30 +02:00
paul cannon
72189330fd satellite/gracefulexit: revamp graceful exit
Currently, graceful exit is a complicated subsystem that keeps a queue
of all pieces expected to be on a node, and asks the node to transfer
those pieces to other nodes one by one. The complexity of the system
has, unfortunately, led to numerous bugs and unexpected behaviors.

We have decided to remove this entire subsystem and restructure graceful
exit as follows:

* Nodes will signal their intent to exit gracefully
* The satellite will not send any new pieces to gracefully exiting nodes
* Pieces on gracefully exiting nodes will be considered by the repair
  subsystem as "retrievable but unhealthy". They will be repaired off of
  the exiting node as needed.
* After one month (with an appropriately high online score), the node
  will be considered exited, and held amounts for the node will be
  released. The repair worker will continue to fetch pieces from the
  node as long as the node stays online.
* If, at the end of the month, a node's online score is below a certain
  threshold, its graceful exit will fail.

Refs: https://github.com/storj/storj/issues/6042
Change-Id: I52d4e07a4198e9cb2adf5e6cee2cb64d6f9f426b
2023-09-27 08:40:01 +00:00
Jeremy Wharton
3d3785a605 web/satellite/vuetify-poc: prevent inappropriate sidebar closure
This change prevents the navigation sidebar from closing when an item
in the My Account dropdown menu is clicked and the display size is
larger than medium.

Resolves #6332

Change-Id: Id37c3d8ee7179805cfecbd3eac9257130e9acc5b
2023-09-26 19:31:12 +00:00
Moby von Briesen
f63f3f19ee satellite/analytics: Do not set lifecyclestage from Segment
We set lifecyclestage in Hubspot without Segment now. If Segment tries
to set lifecyclestage, it interferes with the desired behavior in
Hubspot.

Change-Id: I817c0324ecc69529d8ca7f617cb97d2f4e84aee8
2023-09-26 17:10:22 +00:00
Márton Elek
0affe03007 cmd/tools/tag-signer: make the output less noisy
This patch removes the following lines from the output (with disable tracing + set the log level to warn):

```
2023-09-25T15:30:58+02:00	INFO	process/tracing.go:73	Anonymized tracing enabled
2023-09-25T15:30:58+02:00	DEBUG	tracing collector	monkit-jaeger@v0.0.0-20220915074555-d100d7589f41/udp.go:128	started
2023-09-25T15:30:58+02:00	DEBUG	process/debug.go:37	debug server listening on 127.0.0.1:34803
```

Change-Id: Iccbf4fc3bde9436e0571943d0d85c51ebc766ef9
2023-09-26 16:30:48 +00:00
Vitalii
05a276ecc7 web/satellite/vuetify-poc: add buckets table to project dashboard
Added buckets table to the bottom of project dashboard view

Issue:
https://github.com/storj/storj/issues/6322

Change-Id: I059bf60631096956d55522da7d18cb8a9eaedc93
2023-09-26 15:49:47 +00:00
Vitalii
f5af0f2268 web/satellite/vuetify-poc: add geographic distribution dialog
Added geographic distribution dialog to object browser preview.

Issue:
https://github.com/storj/storj/issues/6321

Change-Id: Ia3d89a3c27ce8100a3a0a4e5fff91f0c41d0595e
2023-09-26 15:49:06 +00:00
Vitalii
f78cde93ac web/satellite/vuetify-poc: make project table search field be consistent with other search fields
Made search field styling be consistent with other search fields.

Issue:
https://github.com/storj/storj/issues/6320

Change-Id: I21e383cef522a9f76b437a8f9977eab72987766c
2023-09-26 15:09:41 +00:00
Cameron
8689f609d7 web/satellite: vuetify upload progress panel
vuetify snackbar with expansion panels displaying upload statuses.

https://github.com/storj/storj/issues/6257

Change-Id: Ife01616f5a07a4987153ef85331ff71f53b8cf78
2023-09-26 14:04:40 +00:00
Ivan Fraixedes
6555a68fa9 satellite/admin: Serve back-office static UI
Serve the front-end sources of the new back-office through the current
satellite admin server under the path `/back-office`.

The front-end is served in the same way than the current one, which is
through an indicated directory path with a configuration parameter or
embed in the binary when that configuration parameter is empty.

The commit also slightly changes the test that checks serving these
static assets for not targeting the empty file in the build folder.

build folders must remain because of the embed directive.

Change-Id: I3c5af6b75ec944722dbdc4c560d0e7d907a205b8
2023-09-26 13:18:29 +00:00
Ivan Fraixedes
48d7be7eab
private/apigen: Panic types defined in main package
The API generator was generating invalid code when types were defined in
a main package because the generated Go code was defining in import from
it.

This commit update the Go generator to panic with a explicit error
message if that situation happens.

The commit also add a new endpoint to the example with a named types
(i.e. no anonymous) to show that the Generator works fine with them.

Change-Id: Ieddd89c67048de50516f7ac7787d602660dc4a54
2023-09-26 14:04:33 +02:00
Ivan Fraixedes
7ab7ac49c8
private/apigen: Remove TypeScript errors from generated code
Remove the "duplicate identifier: 'path'" TypeScript errors from the
generated code.

Change-Id: I6b411a5cee720e2a16353034627954616c480f8a
2023-09-26 14:04:33 +02:00
Ivan Fraixedes
00484429d6
private/apigen: Generate valid TypeScript code with anonymous types
The API generator didn't generate valid TypeScript code when using
Go anonymous types.

This commit fixes that issue creating names for anonymous types.

Change-Id: Ice0748d8650686e3d3979523b8f218dc20eade5a
2023-09-26 14:04:33 +02:00
Ivan Fraixedes
a9901cc7d0
private/apigen: Add validations to improve usage
Add a few validations to panic with a nicer message or abort rather than
generating invalid code.

Also improve the panic message wrapping a standard error with errs2 at
the time that it's returned.

Change-Id: I1393933eb5f0bc3f86646bf4d0acfc64626efbe0
2023-09-26 14:04:33 +02:00
Ivan Fraixedes
822a13570e
private/apigen: Document Endpoint fields
Add document comments to tall the fields of the Endpoint struct.

Change-Id: Ibcd534f26474fc73fb378a9f30ce7d9401217557
2023-09-26 14:04:32 +02:00
Wilfred Asomani
d67bb4f2c5 web/satellite/vuetify-poc: show file delete confirmation dialog
This change extends the folder deletion modal to work for objects as
well.

Issue: https://github.com/storj/storj/issues/6299

Change-Id: I13e9ffa508c802480c0e3ed2ac630fa693b66fc7
2023-09-26 11:00:16 +00:00
Vitalii
4cbfa28e10 web/satellite/vuetify-poc: use 'demo-bucket' placeholder instead of prefilled value
Use placeholder instead of prefilled value for bucket creation dialog.

Issue:
https://github.com/storj/storj/issues/6319

Change-Id: I86c25926034adbe93a58df56e0bf18b60b41e568
2023-09-26 09:59:10 +00:00
Vitalii
4a9d5edbfc web/satellite/vuetify-poc: another round of small improvements
break text of download notification (e.g. when you download from preview)
hide right/left buttons in object preview if there are not next/previous items
add counter property to project name in "project create". Set max length so that user cannot type additional characters

Issue:
https://github.com/storj/storj/issues/6268

Change-Id: Icff95427a5c73c2fb5bb014ff09150283cc49e83
2023-09-26 11:37:21 +03:00
Ivan Fraixedes
9a06de9058 private/apigen: Add test for Types.All
Add a test for Types.All method because we may need to adjust the logic
in future commits and we want to detect what has changed for good or
bad.

Change-Id: I1db4bf67db3c87513cb9aeb7b942c6c132fc4dd1
2023-09-25 21:15:06 +00:00
Antonio Franco
fe0b5743b0
Af testplan/private cloud (#6265)
* doc(testplan): storj private cloud

* Update storj-private-cloud-testplan.md

this commit pushes the content to the template file and removes unused sections.
2023-09-25 18:44:39 +02:00
paul cannon
1b8bd6c082 satellite/repair: unify repair logic
The repair checker and repair worker both need to determine which pieces
are healthy, which are retrievable, and which should be replaced, but
they have been doing it in different ways in different code, which has
been the cause of bugs. The same term could have very similar but subtly
different meanings between the two, causing much confusion.

With this change, the piece- and node-classification logic is
consolidated into one place within the satellite/repair package, so that
both subsystems can use it. This ought to make decision-making code more
concise and more readable.

The consolidated classification logic has been expanded to create more
sets, so that the decision-making code does not need to do as much
precalculation. It should now be clearer in comments and code that a
piece can belong to multiple sets arbitrarily (except where the
definition of the sets makes this logically impossible), and what the
precise meaning of each set is. These sets include Missing, Suspended,
Clumped, OutOfPlacement, InExcludedCountry, ForcingRepair,
UnhealthyRetrievable, Unhealthy, Retrievable, and Healthy.

Some other side effects of this change:

* CreatePutRepairOrderLimits no longer needs to special-case excluded
  countries; it can just create as many order limits as requested (by
  way of len(newNodes)).
* The repair checker will now queue a segment for repair when there are
  any pieces out of placement. The code calls this "forcing a repair".
* The checker.ReliabilityCache is now accessed by way of a GetNodes()
  function similar to the one on the overlay. The classification methods
  like MissingPieces(), OutOfPlacementPieces(), and
  PiecesNodesLastNetsInOrder() are removed in favor of the
  classification logic in satellite/repair/classification.go. This
  means the reliability cache no longer needs access to the placement
  rules or excluded countries list.

Change-Id: I105109fb94ee126952f07d747c6e11131164fadb
2023-09-25 09:42:08 -05:00
Márton Elek
c44e3d78d8 satellite/satellitedb: repairqueue.Select uses placement constraints
Change-Id: I59739926f8f6c5eaca3199369d4c5d88a9c08be8
2023-09-25 10:14:25 +00:00
Márton Elek
b4fdc49194 satellite/repair/checker: persist placement information to the queue
Change-Id: I51c7fd5a2a38f9f6620c16eddaed3b4915ffd792
2023-09-25 09:33:46 +00:00
Márton Elek
18d5caad7e satellite/satellitedb: write/read placement information to/from repairqueue
Change-Id: Ie58f129feae7898850905940f94643605dcf56ae
2023-09-25 08:52:56 +00:00
Vitalii
7d0b8f6f8c web/satellite/vuetify-poc: center loader in file preview
Vertically centered loader and no preview states for gallery view.

Issue:
https://github.com/storj/storj/issues/6312

Change-Id: Icce4aff6d0927ee36e3a94886edf4cc31dd379bf
2023-09-23 05:11:51 +00:00
Vitalii
5689083393 web/satellite/vuetify-poc: fix limits values formatting on project dashboard
Fixed regular expression which should search and remove insignificant trailing zeros of limit values.

Issue:
https://github.com/storj/storj/issues/6311

Change-Id: I267c779e406a1933d43f09b497470bad9f8ab71c
2023-09-22 21:16:54 +00:00
Vitalii
52d337496f web/satellite: change 'Bandwidth' and 'Egress' labels to 'Download' on project dashboard/settings
Updated label to be 'Download' instead of 'Bandwidth' or 'Egress'

Issue:
https://github.com/storj/storj/issues/6310
https://github.com/storj/storj/issues/6315

Change-Id: Id075882e8dd1e243ca516907d9db420d6d066437
2023-09-22 20:36:22 +00:00
Vitalii
a43f17d9a3 web/satellite/vuetify-poc: use 'Files' header instead of 'Objects' for buckets table
Updated table header to be 'Files' instead of 'Objects'

Issue:
https://github.com/storj/storj/issues/6309

Change-Id: I65058b473481e8b7218db15767c5cb7e2e0c2d8d
2023-09-22 19:01:10 +00:00
Ivan Fraixedes
8381483f79
satellite/admin: Add back-office UI sources
Add the front-end sources of the new back-office.

The front-end doesn't have any business logic, it only has the pages and
the components, so it's purely UI.

The front-end was developed in a separate repository until was
completed.

Change-Id: I382e50789d6b929a67b8a0b887563ef48cb1473d
2023-09-22 18:02:55 +02:00
Dan Willoughby
e563de6c81 web/satellite: Update segment and free tier doc links
4 links updated

Change-Id: I71111079b58542e8c7e4240e2cb55414a7b35934
2023-09-22 11:33:58 +00:00
Jeremy Wharton
5f4cd92cc5 web/satellite/vuetify-poc: clear S3 data when switching projects
This change fixes an issue where the S3 client data was not cleared
when switching projects. This would cause errors to appear when
entering the bucket of a project you switched to.

Resolves #6295

Change-Id: Ib9da43ddf1d38eed6ca26ba73a24e38815617b3e
2023-09-22 00:49:22 -05:00
Jeremy Wharton
e92acca937 web/satellite/vuetify-poc: show loader when creating a folder
This change makes the New Folder dialog indicate its loading status.

Resolves #6307

Change-Id: I37c87ce78f5dfa0d2594e874f248c451fb0d710f
2023-09-21 23:08:24 +00:00
Jeremy Wharton
0205a08c20 web/satellite/vuetify-poc: use image icon for SVGs in object browser
This change makes SVG file entries in the object browser table use the
image icon. Previously, SVGs used the icon for unknown file types.

Resolves #6306

Change-Id: Ic2a8b6154dc222292f3048d967a0420d5872acd0
2023-09-21 22:27:45 +00:00
Jeremy Wharton
6a4abb7f14 web/satellite/vuetify-poc: make project stat cards clickable
When clicked, cards containing project stats will redirect the user to
the card's respective page.

Resolves #6305

Change-Id: I6a598ad2a8a6ab79f48f559eced55f8f8257a518
2023-09-21 21:47:22 +00:00
Jeremy Wharton
15c0c675b1 web/satellite/vuetify-poc: update "Learn More" links in page subtitles
This change updates the "Learn More" links in the Buckets page,
the Team page, and the project dashboard.

Resolves #6293

Change-Id: I2d9c0ca9b8bbd2991869648d231a8069868efdc0
2023-09-21 21:06:57 +00:00
Jeremy Wharton
123309c648 web/satellite: update vuetify dependency
This change updates the Vuetify dependency from 3.3.12 to 3.3.17.

Change-Id: Id570e28c105a10fb2baca25367a4a3e53ce4bf52
2023-09-21 15:55:40 +00:00
Márton Elek
98921f9faa satellite/overlay: fix placement selection config parsing
When we do `satellite run api --placement '...'`, the placement rules are not parsed well.

The problem is based on `viper.AllSettings()`, and the main logic is sg. like this (from a new unit test):

```
		r := ConfigurablePlacementRule{}
		err := r.Set(p)
		require.NoError(t, err)
		serialized := r.String()

		r2 := ConfigurablePlacementRule{}
		err = r2.Set(serialized)
		require.NoError(t, err)

		require.Equal(t, p, r2.String())
```

All settings evaluates the placement rules in `ConfigurablePlacementRules` and stores the string representation.

The problem is that we don't have proper `String()` implementation (it prints out the structs instead of the original definition.

There are two main solutions for this problem:

 1. We can fix the `String()`. When we parse a placement rule, the `String()` method should print out the original definition
 2. We can switch to use pure string as configuration parameter, and parse the rules only when required.

I feel that 1 is error prone, we can do it (and in this patch I added a lot of `String()` implementations, but it's hard to be sure that our `String()` logic is inline with the parsing logic.

Therefore I decided to make the configuration value of the placements a string (or a wrapper around string).

That's the main reason why this patch seems to be big, as I updated all the usages.

But the main part is in beginning of the `placement.go` (configuration parsing is not a pflag.Value implementation any more, but a separated step).

And `filter.go`, (a few more String implementation for filters.

https://github.com/storj/storj/issues/6248

Change-Id: I47c762d3514342b76a2e85683b1c891502a0756a
2023-09-21 14:31:41 +00:00
Jeremy Wharton
950672ca6c web/satellite/vuetify-poc: implement Manage Passphrase dialog
This change implements the Manage Passphrase dialog in the Vuetify
project. Within it, users can create, switch, or clear the current
passphrase.

Resolves #6284

Change-Id: I2ca87e62b59c0cefd13bf36005c9301b35f12255
2023-09-20 18:36:21 -05:00
Jeremy Wharton
471111122b web/satellite/vuetify-poc: implement Delete Access dialog
This change implements the Delete Access dialog for deleting access
grants. It is triggered by clicking Delete in the actions menu of
access grant table rows.

Resolves #6300

Change-Id: I288f9a88c62e57390f039e41ca6770d2942a9058
2023-09-20 22:08:30 +00:00
Cameron
22261146be cmd/uplink: don't output stacktrace with unknown access
A stacktrace is unnecessary for this error, so don't print one.

https://github.com/storj/storj/issues/5910

Change-Id: Id9e5d9d188042cc7ac0dba571138d5f3e331a9d6
2023-09-20 10:06:47 +00:00
Ivan Fraixedes
926076bffd private/apigen/example: Add description to endpoint
Add the description field to the endpoint to show where it ends up when
generating code.

Change-Id: I415f088dbf795656ed1ef042ed41ebf39a517692
2023-09-19 18:31:20 +00:00
Wilfred Asomani
e599df03a8 satellite/console: add more cross-user api tests
This change adds more endpoints to the cross-user api test.

Issue: https://github.com/storj/storj/issues/6246

Change-Id: I4c3128c3a932c713b10499a8909836a599b79458
2023-09-19 16:50:31 +00:00
Cameron
a2d37bc69a {satellite/console, web/satellite}: send analytics if invitee signs up
send analytics event if project invite link is clicked and if user
signs up.

github issue: https://github.com/storj/storj/issues/5190

Change-Id: I41eee5e679a84b9ec325815655684a98624d5656
2023-09-19 14:50:44 +00:00
Vitalii
81d49ada06 web/satellite: slightly refactored billing modals
Slightly refactored html and css code (it's still weird) of billing modals.
Probably there is no reason to rework it entirely since we're going to migrate to Vuetify project.

Issue:
https://github.com/storj/storj/issues/5220

Change-Id: I25b4b0cdb9d4d24ef3d1f615f2a3471b2d3e727d
2023-09-19 14:01:31 +00:00
Wilfred Asomani
0a063fdeb3 web/satellite/vuetify-poc: require MFA code to generate MFA recovery codes
This change uses the code protected MFA code generation endpoint. It
requires a code from the user before generating new recovery codes.

Issue: https://github.com/storj/storj-private/issues/433

Change-Id: I38c7c6f543a1d0c68aa1c2e9092e76fed2448467
2023-09-19 08:28:10 +00:00
Márton Elek
5bbd477a58 go.mod: bump dependencies (uplink,common,monkit)
Change-Id: I72e17c3acbabd36ffb0a0adbcccec4c252ba710c
2023-09-19 00:35:07 +00:00
Jeremy Wharton
3104a830ae web/satellite/vuetify-poc: unmock list of owned and shared projects
This change shows real projects in the Project dropdown of the project
navigation sidebar, replacing mock data.

Resolves #6283

Change-Id: Id8eef6cc02b5b773f89a00d41d5335ae2feb1729
2023-09-18 23:53:43 +00:00
Michal Niewrzal
5d0934e4d9 satellite/metabase/rangedloop: disable ranged loop for tests
Currently each testplanet test is running ranged loop no matter if
it's used or not. This is small change with some benefits like:
* saves some cpu cycles
* less log entries
* ranged loop won't interfere with other systems

Change have no big impact on tests execration but I believe it's nice to
have.

Change-Id: I731846bf625cac47ed4f3ca3bc1d1a4659bdcce8
2023-09-18 22:51:10 +00:00
Jeremy Wharton
95d87f5a22 web/satellite/vuetify-poc: remove Browse from project nav sidebar
This change removes the Browse item from the project navigation
sidebar. This item didn't do anything, and we have no plans to use it.

Resolves #6281

Change-Id: Ifed6a563e38585955b0bf180c2896d6d224cb7dc
2023-09-18 21:58:26 +00:00
Andrew Harding
fd679c329c private/server: FreeBSD TCP fastopen support detection
Detects whether or not TCP fastopen is supported by running sysctl to
grab the current value of net.inet.tcp.fastopen.server_enable.

If not enabled, directs the operator to use sysctl to set it temporarily
and /etc/sysctl.conf to set it on-boot.

Setting the socket option always works whether it is enabled or not.

Verified that fastopen works on FreeBSD 13. Did not attempt on earlier
versions but support has been there since FreeBSD 10.

Change-Id: I2e0c457558a6fa7b7a1b18bc3c6684aff50b81a2
2023-09-18 21:17:42 +00:00
Jeremy Wharton
ad5c2e171a web/satellite/vuetify-poc: add tier indicator
This change indicates the user's tier in the My Account dropdown button
of the Vuetify project.

Resolves #6278

Change-Id: I0db6bfe8e03720b87ff77e947f785031eed7e528
2023-09-18 15:25:53 -05:00
Egon Elbre
c48f58e968 satellite/metabase: simplify BeginExactObject
There's only one value that BeginExactObject should return and there's
no point in restating that.

Also, use a clearer value for the random object version.

Change-Id: I06b26ad87d64e1b04b48458f624edd630f7f2f1d
2023-09-18 19:26:49 +00:00
Jeremy Wharton
109c0d5e37 web/satellite/vuetify-poc: make Resources dropdown items functional
This change adds functionality to the items in the project navigation
sidebar's Resources dropdown menu.

Resolves #6282

Change-Id: I64e0d472592ec07545034adff6a3c2122fcc113c
2023-09-18 18:47:05 +00:00
Jeremy Wharton
012e1fbc06 cmd/uplink: use configured user agent
This change fixes an issue where the configured user agent wasn't used
by Uplink commands.

Resolves storj/customer-issues#999

Change-Id: I2d9f38308eddad7c471a100c968082783c05a3b3
2023-09-18 18:00:34 +00:00
Wilfred Asomani
f42548ac1c web/satellite: require MFA code to generate MFA recovery codes
This change uses the code protected MFA code generation endpoint. It
requires a code from the user before generating new recovery codes.

Issue: https://github.com/storj/storj-private/issues/433

Change-Id: I248649567a4800374b84ee512a79195ea2c44652
2023-09-18 16:54:20 +00:00
Wilfred Asomani
8ad0bc5e61 satellite/console: add alt code protected MFA recovery endpoint
This change adds an alternate MFA code recovery endpoint that requires
MFA code to generate codes.

Issue: https://github.com/storj/storj-private/issues/433

Change-Id: I10d922e9ad1ace4300d4bcfea7f48494227f1ff8
2023-09-18 16:07:43 +00:00
Wilfred Asomani
1a8913e7a0 web/satellite/vuetify-poc: improve navigation drawer on small screens
This change improves the behaviour of the navigation drawer. It is now
able to automatically close on resize to smaller screen sizes and vice
versa, and also by close by clicking outside the drawer.

Issue: https://github.com/storj/storj/issues/6266

Change-Id: I1aee465a546abbf1369c48f6827b058523c5da21
2023-09-18 15:02:22 +00:00
Andrew Harding
e4d6829971 private/server: Windows TCP fast-open support detection
Implements tryEnableFastOpen by creating a localhost socket and enabling
the TCP_FASTOPEN socket option. On builds where TCP_FASTOPEN isn't
available, setting the socket option fails and tryEnableFastOpen returns
false.

This was verified on a supporting build (latest Windows 10) and an
unsupporting build (Windows 8.1, couldn't find an ISO for an older
Windows 10 build).

Change-Id: I497117dc2f04acdd2b0cc836e20d12d69076b939
2023-09-18 14:15:30 +00:00
Vitalii
ecc527ad3b web/satellite: low STORJ balance notification
Added a new banner to the dashboard of all projects. This banner is displayed when a user meets the following conditions: they have no credit cards on file, they have a payment history with tokens, and their estimated charges are higher than their current balance.

Issue:
https://github.com/storj/storj/issues/6234

Change-Id: I1f90ae81032d459111b111d23ce2e1d8119e649d
2023-09-18 16:30:59 +03:00
Egon Elbre
bc517cae2f go.mod: bump common
This brings in common/grant that doesn't depend on protobuf anymore.
This ends up causing the console wasm bundle from ~11MB to ~4.7MB.

Change-Id: I145dcb9239952a7a9e352c8793c111acb61ff0cc
2023-09-15 17:43:44 +00:00
Jeremy Wharton
8a0d1e8b55 web/satellite/vuetify-poc: show region in My Account dropdown
This change displays the satellite's region in the My Account dropdown
menu of the Vuetify project.

Resolves #6279

Change-Id: Ib7d739c0104fb20ad7ee23234c66813b7f37b3a5
2023-09-15 16:31:21 +00:00
Wilfred Asomani
1efc0ceaa5 web/satellite: load usage and charges separately
This change separates the loading of usage and charges from other API
calls since that may take long. This will allow the other data to be
displayed while usage and charges are waited on.

Issue: https://github.com/storj/storj/issues/6259

Change-Id: I4a8d8f911baf432d6f1e9eee49176480197ae3ca
2023-09-15 15:50:02 +00:00
Jeremy Wharton
789b37c21f web/satellite/vuetify-poc: remove notification on close button click
This change fixes an issue where a notification would not be completely
removed after clicking the close button, causing it to invisibly prevent
elements beneath it from being clicked. Also, the file preview dialog no
longer closes when a notification is clicked.

Resolves #6280

Change-Id: I135aa2e77ddc1ec845101209f26dde5e48f10bd6
2023-09-15 15:10:48 +00:00
Vitalii
bd36749f7d web/satellite: improve bucket list on small screens
Bucket list in vuetify never requires horizontal scrolling to view the entire row.
Removed non-essential columns from the bucket list when the screen size is small.
Bucket name is the only "essential" piece of information that must always be shown.

Issue:
https://github.com/storj/storj/issues/6232

Change-Id: Id4bee3100f6d4ca112670d2f68bd63ff2dc266e9
2023-09-15 17:28:40 +03:00
Michal Niewrzal
975d953cb8 satellite/metabase: custom error for commit object
We stoped returning lots of errors as is to avoid leaking our internals
but some errors were meanigful for client. Example of such error is
"exceeded maximum number of parts". With this change we are wrapping
some important commit object errors with new ErrFailedPrecondition
error to be able to return it easily to uplink.

Change-Id: Id834b78362ed1920f0c3f6f1c7d9587bfd27e36a
2023-09-15 09:52:42 +00:00
Márton Elek
f4fe983b1e satellite/{placement,nodeselection}: introduce empty() and notEmpty() for tag value selection
It helps to implement rules like `tag("nodeid","select",notEmpty())

Change-Id: If7a4532eacc0e4e670ffe81d504aab9d5b34302f
2023-09-14 19:30:29 +00:00
Vitalii
92a69c7de4 web/satellite: add loader to object browser table
This is a fix based on early feedback from QA team.
Added loader to object browser table so that user can't change pages while request is still in progress because it breaks pagination.

Change-Id: I5cc2ff057955478b3c745c169d520e1a639eff92
2023-09-14 18:50:51 +00:00
Wilfred Asomani
bd48a5cbe6 web/satellite: allow limit increase request
This change implements a modal to request limit increases.

Issue: https://github.com/storj/storj/issues/6233

Change-Id: I40ace89a79c65751547d804e8d190e866217d379
2023-09-14 18:08:56 +00:00
Jeremy Wharton
0f3ff66485 web/satellite: show error for rate limited MFA logins
This change fixes an issue where errors were not displayed for login
attempts that failed due to rate limiting.

Change-Id: Ia3c7fccf434ad62bb252f4215676b1f32903ac53
2023-09-14 15:20:32 +00:00
Michal Niewrzal
881137539c satellite/metabase: commit object is not respecting expiration time
With pending_objects table support enabled we missed passing correctly
expiration time from pending object to committed object. This change
updates commit query to take into account expiration time.

Change-Id: I67146d5b2f7f0bda02925d16275fbc59acb705bd
2023-09-14 16:25:28 +02:00
Vitalii
8f27425284 satellite/{projectaccounting, web}: merge settled and allocated lines for bandwidth graph
Merged bandwidth graph lines to show only allocated-dead for last 3 days and settled for other days.

Issue:
https://github.com/storj/storj/issues/6072

Change-Id: Ic7f03d22ccd82d27ae6e6a85e73e144c9852e33b
2023-09-14 15:39:56 +03:00
Jeremy Wharton
ec8f3b4528 web/satellite/vuetify-poc: allow for sharing files and folders
This change allows linksharing URLs to be generated for files and
folders within the Vuetify project's file browser.

Resolves #6111

Change-Id: I8cbe81b33cb5e35de0c34bba8ccc9175c727bd94
2023-09-14 11:23:08 +00:00
Michal Niewrzal
8f1682941e mod: bump storj/uplink to v1.12.0
Change-Id: Ia58de16d4ac43c7b888000ad8ca85a979496a2cb
2023-09-14 09:20:38 +02:00
Wilfred Asomani
ccb9b7ae8e satellite/web/vuetify-poc: make tables consistent
This change makes tables in the vuetify app more consistent. Also
clearing search has been fixed for tables whose data would not populate
after search has been cleared.

Issue: https://github.com/storj/storj/issues/6267

Change-Id: I053d9e5f23662774c60d67a29f814a2c1c3067ed
2023-09-13 23:15:28 +00:00
Clement Sam
f14fabc90a cmd/storagenode: add forget-satellite subcommand
This change adds a new forget-satellite sub-command to
the storagenode CLI which cleans up untrusted satellite
data.

Issue: https://github.com/storj/storj/issues/6068
Change-Id: Iafa109fdc98afdba7582f568a61c22222da65f02
2023-09-13 19:06:55 +00:00
Wilfred Asomani
dcf3f25f93 satellite/admin: update README
add descriptions for the endpoint that removes a user from the waning
state.

Issue: https://github.com/storj/storj/issues/6118

Change-Id: I211cd3c41c7fefa295d0db1b9f43f53e33b984e6
2023-09-13 17:50:28 +00:00
Jeremy Wharton
7d8b231aaf cmd/satellite: build Vuetify project in Dockerfile
This change updates the satellite Dockerfile to build the Vuetify
project.

References #6251

Change-Id: I699360c0f7eb7a8abdd0bc523ee74910a04fecd3
2023-09-13 12:43:58 +00:00
Michal Niewrzal
fb04a22088 satellite/metainfo: flag to rollout pending_objects table support
To avoid enabling feature for every project at once we would like to
do this partially and control percentage of projects that will have
feature enabled.

https://github.com/storj/storj/issues/6258

Change-Id: Iaac7c42d39da76ed2ecc439847c3b210462befa5
2023-09-13 10:33:23 +00:00
Egon Elbre
d91ee440ba satellite/metabase: use constant for pending version
Currently it wasn't quite clear what was a stub version and an actual
version. Use a PendingVersion constant to make this distinction clear.

Also use PendingVersion = NextVersion = 0, that way it's clearer that
the version hasn't been yet determined. DefaultVersion = 1 might imply
that the object will get that version once commited, however that will
entirely depend on whether use-pending-objects is used or versioning is
enabled or not.

Change-Id: I21398141f97035c48c778f23b542266b834c44f1
2023-09-12 18:01:12 +00:00
Jeremy Wharton
4f8697568d web/satellite: use edge service URL overrides
This change makes the satellite frontend use edge service URL overrides
if they have been configured for a project.

Resolves #6188
Resolves #6190

Change-Id: I4c8fb3f5f00f450fb8cd139383972ab622234fb0
2023-09-12 12:19:20 -05:00
Jeremy Wharton
c8f4f5210d satellite/console: return edge URL overrides in project info responses
API responses containing project information now contain the edge
service URL overrides configured for that project. The overrides are
based on the project's default placement.

References #6188

Change-Id: Ifc3dc74e75c0f5daf0419ac3be184415c65b202e
2023-09-12 12:10:18 -05:00
Clement Sam
89d682f49f satellite/metainfo: prevent internal DB errors in Public API
Resolves https://github.com/storj/storj/issues/6081

Change-Id: I0e530db39947138dcafc1b6bd1710ff1ca96b8c5
2023-09-12 15:12:44 +00:00
Michal Niewrzal
e21978f11a satellite/metainfo: support getting specific object version
Protobuf definition is read to support getting specific version of
object so we just need to wire requested version into metainfo.GetObject
endpoint.

https://github.com/storj/storj/issues/6221

Change-Id: If4568b82119a6c893788a0a86e598b05ff5951cf
2023-09-12 14:27:40 +00:00
Vitalii
4e4da7be6d web/satellite/vuetify-poc: always show details container under validatable inputs
Inputs that have some custom validation messaging attached should not use hide-details=auto to prevent 'jumpy' visual experience.

Issue:
https://github.com/storj/storj/issues/6230

Change-Id: Ia90e122516eb853a3908c0f57634971243fb38b3
2023-09-12 13:47:22 +00:00
Jeremy Wharton
088496efdf web/satellite/vuetify-poc: allow for deleting files and folders
This change allows files and folders to be deleted from within the
Vuetify project's file browser.

Resolves #6106

Change-Id: I0d7b0528b08333aeec29917c4ebef6ea966ac1fa
2023-09-12 13:08:09 +00:00
Egon Elbre
28ee6f024c satellite: don't use fmt.Print in tests
Change-Id: Ia10450240ad075c9d78614adff9164f292fb1fa0
2023-09-12 15:11:02 +03:00
Márton Elek
afa5c54a35 satellite/satellitedb: add placement column to repair_queue
It makes it possible to run dedicated repair worker for different placement definitions.

https://github.com/storj/storj-private/issues/400

Change-Id: I376da867da5dbb4ab392d5f86c766f7543c32ee6
2023-09-12 11:29:18 +00:00
Vitalii
2cf233ac23 web/satellite/vuetify-poc: hide nav sidebar by default for screens less than 1280px
On screen sizes where the sidebar overlaps with the content of the page, the sidebar should be collapsed by default.

Issue:
https://github.com/storj/storj/issues/6229

Change-Id: Ia0a91acd95519de27f9ff8f1ee90c6b8e7932266
2023-09-12 13:48:53 +03:00
Cameron
f9ab2f0de7 satellite/console: prefix oidc paths with /api/v0/
add /api/v0/ prefix to oidc paths to route requests to the api
pod with split UI infrastructure.

Change-Id: I1e7e691487ab1d4e84434d204d10b7f944ae8873
2023-09-11 18:12:39 +00:00
Egon Elbre
487f64e164 satellite/satellitedb,multinode/multinodedb: update to latest dbx
Change-Id: I500df6d0541706c3960d4560721c3783d0d049ff
2023-09-11 17:21:02 +00:00
Egon Elbre
8edb9c5f98 multinode/multinodedb: don't generate rx
Change-Id: Ica1a2bb1ab42e881daa951ab61b1dd99e877aff5
2023-09-11 16:40:52 +00:00
Egon Elbre
87bfb3b02b satellite/satellitedb: don't generate rx
I'm not sure what Rx is, however, we aren't using it --
so let's remove it for now.

Change-Id: I9caacbc150479f93945477101528a4fd60ea865f
2023-09-11 16:00:15 +00:00
Egon Elbre
3e73d414d1 satellite/console/consoleweb: initialize mime lazily
Change-Id: I80b78edcf057acef9b5a599cb77308baddc07692
2023-09-11 15:19:56 +00:00
Michal Niewrzal
0a3ee6ff8a satellite/metabase: remove old object segments on overwrite
While adding support for pending_objects table one case was missed.
When we are uploading object to location where old objects exists
we are not removing old object segments at all. Old object is
overwritten with new object metadata but segments remains without
corresponding object. This fix removes all existing committed objects
(with it's segments) before committing new object.

https://github.com/storj/storj/issues/6255

Change-Id: Id657840edf763fd6aec8191788d819191b074fb7
2023-09-11 14:16:47 +00:00
Vitalii
c31fb9c1cf satellite/payments: restrict addition of duplicate credit cards
By this change we don't allow users to add credit cards that are already bind to their account.
We still allow the same CC number but with a different expiration date.

Issue:
https://github.com/storj/storj/issues/5597

Change-Id: Ifeb0cc5ae0c2f0f7596af4dead70ae7d20d30613
2023-09-11 13:24:43 +03:00
Kaloyan Raev
3119b614ae cmd/uplink: --max-object-ttl flag for share and access restrict commands
Context: https://github.com/storj/storj/issues/6249

Change-Id: Ic65a1d8aef61f1a88752a7b12a23fb854dac8f6d
2023-09-11 08:14:55 +00:00
Vitalii
9254dd2208 web/satellite/vuetify-poc: enable object browser pagination
Enabled object browser pagination for vuetify app.
Also fixed some small bug when returning to first page.

Issue:
https://github.com/storj/storj/issues/5595

Change-Id: I8b5e90a4cd7d7a79a8beeb292b7374db3f93d700
2023-09-09 00:36:55 +00:00
Wilfred Asomani
2bf4113821 web/satellite/veutify-poc: create folder
This change adds the ability to create folders in the file browser.

Issue: https://github.com/storj/storj/issues/6105

Change-Id: I0dae0f9874b571cfd0ae79b2b994b58149d70aa3
2023-09-08 23:57:13 +00:00
Wilfred Asomani
54379fc0ee satellite/{console,analytics}: allow limit increase request
This change adds a  new endpoint that submits limit increase requests
to segment.

Issue: https://github.com/storj/storj/issues/6233

Change-Id: Ie4f70aef31079acbe2f24771b3ea359d5769eb95
2023-09-08 23:17:38 +00:00
Wilfred Asomani
cd7d9cf079 satellite/{console,payments}: unfreeze user on token payment
This change unfreezes/unwarns users in either state after successful
payment with tokens.

Change-Id: I7494d6a33cf9383576f42af695df522d8f409e03
2023-09-08 12:51:01 +00:00
Cameron
c52554a2b9 web/satellite: vuetify upload file and folder
add functionality to upload files and folders in object browser

issue: https://github.com/storj/storj/issues/6101
issue: https://github.com/storj/storj/issues/6102

Change-Id: I4aa86b89adc051b91b0d7fde69dd7375b2a3f370
2023-09-08 11:47:01 +00:00
Kaloyan Raev
4e499fb9bf satellite/metainfo: respect MaxObjectTTL in BeginObject
If MaxObjectTTL is set in the API key, BeginObject will use it for the
object expiration time, unless an explicit ExpireAt is available in the
request.

Context: https://github.com/storj/storj/issues/6249

Change-Id: I2adf57d979a9c68eec3a787f3739d2f1dbec1f7e
2023-09-08 09:30:48 +00:00
Moby von Briesen
8d1a765fd6 satellite/console: Partially revert change to remove graphql
This partially reverts commit 516241e406.

Endpoints are added to the backend, as there are some customers who may
use these endpoints, even though they are no longer necessary for the
satellite UI.

Change-Id: I52a99912d9eacf269fbb2ddca603e53c4af6d6bf
2023-09-07 20:50:24 +00:00
Wilfred Asomani
754bf5f8af Revert "satellite/db: optimize project usage query"
This reverts commit 31ec421299.

This change made the usages endpoint slower for accounts with large
number of projects.

Change-Id: I95870e95c2bf3bc3050087532fd0d20cbb50748b
2023-09-07 19:27:08 +00:00
Michal Niewrzal
df037564d7 satellite/zombiedeletion: remove inactive uploads from pending_objects
With zombie deletion chore we are removing inactive pending objects from
objects table but new we need also to do this for pending_objects table.

https://github.com/storj/storj/issues/6050

Change-Id: Ia29116c103673a1d9e10c2f16654022572210a8a
2023-09-07 18:47:29 +00:00
Márton Elek
ad87d1de74
satellite/satellitedb/overlaycache: fill node tags with join for limited number of nodes
The easiest way to get node information WITH node tags is executing two queries:

 1. select all nodes
 2. select all tags

And we can pair them with a loop, using the in-memory data structures.

But this approach does work only, if we select all nodes, which is true when we use cache (upload, download, repair checker).

But repair process selects only the required nodes, where this approach is suboptimal. (full table scan for all tags, even if we need only tags for a few dozens nodes).

Possible solutions:

 1. We can introduce a cache for repair (similar to upload cache)
 2. Or we can select both node and tag information with one query (join).

This patch implements the second approach.

Note: repair itself is quite slow (10-20 seconds per segements to repair). With 15 seconds execution time and 3 minutes cache staleness, we would use the cache only 12 times per worker. Probably we don't need cache for now.

https://github.com/storj/storj/issues/6198

Change-Id: I0364d94306e9815a1c280b71e843b8f504e3d870
2023-09-07 19:27:53 +02:00
Kaloyan Raev
82b108de69 satellite/console/consolewasm: no direct cast to grant.Permission
A new field is introduced to grant.Permission in storj.io/common. Having
a direct cast here leads to compilation problems when bumping
storj.io/uplink to the latest storj.io/common. Avoiding the direct cast
resolves the issue.

Context: https://github.com/storj/storj/issues/6249

Change-Id: I3b9bc14ebcce8e192e218c621b996300753b8de4
2023-09-07 13:27:53 +03:00
Moby von Briesen
6195b8cd52 satellite/admin: support more options for passing project ID
This change does two things:
* allow using either public ID or private ID to do project-related
  requests in admin UI
* allow passing a UUID string not containing dashes (i.e. a pure hex
  string) in order to do project-related requests in admin UI

Change-Id: I4807a5d7252a48f4a09e3966c406645d55c856e2
2023-09-07 08:53:41 +00:00
Dan Willoughby
091c72319a web/satellite: update welcome email links
Change-Id: Ic918a7ec30db1753d780643492704fc10aafeb99
2023-09-06 15:18:51 -06:00
Wilfred Asomani
7311d08139 web/satellite/vuetify-poc: upload via drag and drop
This change adds the drag-drop upload feature to the vuetify app.

Issue: https://github.com/storj/storj/issues/6104

Change-Id: I177e33a677d94db9ef95a31e32da853a46a7dc51
2023-09-06 19:20:50 +00:00
Vitalii
623b989973 web/satellite/vuetify-poc: unmock STORJ txs table on billing screen
Show real STORJ token transactions on billing screen in vuetify app.

Issue:
https://github.com/storj/storj/issues/6098

Change-Id: I1d7c2a613fefbf68c7ce3b8f62ec7ee992885bc4
2023-09-06 18:31:05 +03:00
Vitalii
6e1fd12930 web/satellite: paginate ListObjects request to show more than 1000 objects
With this change, we are able to fetch all objects to show in the object browser.
AWS SDK V3 provides paginator functionality to automatically make additional requests for every MaxKeys value (we use 500 objects at a time).
By initial request we fetch first 500 objects and save continuation tokens for the rest of the object batches.
Also, we save currently active (fetched) object range.
If user tries to open a page with objects which are out of currently active range then we look for needed continuation token and fetch needed objects batch.
Added a feature flag for this funtionality.

Issue:
https://github.com/storj/storj/issues/5595

Change-Id: If63e3c2ddaac3ea9f2bc1dc63cb49007f897e3e2
2023-09-06 12:47:15 +00:00
Márton Elek
f40baf8629
go.mod: bump dependencies (private,uplink,common)
Change-Id: I6c55735b45cadaf36697eff53e78b5b09afe9dea
2023-09-06 13:28:22 +02:00
Clement Sam
9ab934e2ae storagenode/piecestore: implement trash recovery for download requests
This change allows a node to look for a piece in the trash when
serving a download request.

If the piece is found in the trash, it restores it to the blobs
directory and continue to serve the request as expected.

Resolves https://github.com/storj/storj/issues/6145

Change-Id: Ibfa3c0b4954875fa977bc995fc4dd2705ca3ce42
2023-09-05 23:04:21 +00:00
Wilfred Asomani
3f1ea4a0b9 web/satellite/vuetify-poc: list invoices
This change lists invoices on the vuetifypoc billing history tab.
It also removes invoice filtering on the main app.

Issue: https://github.com/storj/storj/issues/6099

Change-Id: Id4cc2db003a0208775ddaefc87abf26f4b05106c
2023-09-05 15:01:16 +00:00
Wilfred Asomani
775db4aa3c satellite/payments: filter out draft invoices
This change filters the list of invoices sent to the frontend to only
contain open or paid invoices.

Change-Id: I9ac2640a7587a76b0baf46d941f799e742aa2b3b
2023-09-05 14:22:30 +00:00
Jeremy Wharton
4e3e31a425 satellite/console/consoleweb/consoleapi: refactor api requests in tests
This change refactors the way requests are sent in console API tests,
placing identical logic in a dedicated function to reduce code
duplication.

Change-Id: I7a5ac42d8d68a3fd9a9f8b9d61775659234e883f
2023-09-05 11:17:02 +00:00
Wilfred Asomani
2d18f43b6a web/satellite/vuetify-poc: show file previews
This change enables files to be previewed.

Issue: https://github.com/storj/storj/issues/6108

Change-Id: I3045950281822620de96b86e0180eb0d24e2d629
2023-09-05 10:35:31 +00:00
Márton Elek
e2006d821c satellite/overlay: change Reliable and KnownReliable
as GetParticipatingNodes and GetNodes, respectively.

We now want these functions to include offline and suspended nodes as
well, so that we can force immediate repair when pieces are out of
placement or in excluded countries. With that change, the old names no
longer made sense.

Change-Id: Icbcbad43dbde0ca8cbc80a4d17a896bb89b078b7
2023-09-02 23:34:50 +00:00
Cameron
6896241933 satellite/console: fix unverified invite email
When checking if invited user is unverified, initialize oldest
with unverified row rather than empty User, because empty User
CreatedAt is zero, so no real user could be created earlier.

Change-Id: I74dd8f7fc82951cbb61071632a74b1a9443b41fe
2023-09-01 15:11:58 -04:00
Michal Niewrzal
c9591e9754 satellite/metainfo: better metabase errors handling
Some errors were returned as metabase errors, not pure drpc
errors because of how rpcstatus.Code method is working. Status
code was returned for errors like metabase context canceled but
we would like to not leak our internals to the client.

Change-Id: I3f0194755f8d7359b1e3d342fa3be3d984019ecb
2023-09-01 11:32:10 +00:00
Moby von Briesen
b4c95a3b28 private/apigen/tsgen: Set query params better
The old way did not properly handle escaping, e.g. if the value of a
query param contained `&` or `=` inside it. By using
url.searchParams.set, we can safely add these types of arguments to the
path.

Change-Id: I62d3883b14f9d5a517e4a3d58f019014b46fd1b4
2023-08-31 20:09:29 -04:00
Jeremy Wharton
28d498f91d web/satellite/vuetify-poc: allow file browser downloads
This change allows files to be downloaded from within the file browser
of the Vuetify project.

Resolves #6107

Change-Id: I0ac0384711baccb99c0a6d382fe96f318290789b
2023-08-31 22:41:52 +00:00
Jeremy Wharton
b671641a28 satellite/console: update CSP to include storjapi.io
This change updates our content security policy to include the domain
storjapi.io and all of its subdomains.

References #6188

Change-Id: I6f3073bc32aa99626c54caf00bf07d2253ccbb8f
2023-08-31 22:02:45 +00:00
Márton Elek
c202929413
satellite/nodeselection: rename (NodeFilter).MatchInclude to Match
As I learned, the `Include` supposed to communicate that some internal change also "included" to the filters during the check -> filters might be stateful.

But it's not the case any more after 552242387, where we removed the only one stateful filter.

Change-Id: I7c36ddadb2defbfa3b6b67bcc115e4427ba9e083
2023-08-31 16:17:52 +02:00
Wilfred Asomani
dcc4bd0d10 satellite/{console,payments}: freeze/warn storjscan users
This change enables the freezing/warning of users who use storjscan.

Issue: https://github.com/storj/storj/issues/6164

Change-Id: I7b00ee09d6527b3818b72326e9065c82ef5a2ac8
2023-08-31 13:22:21 +00:00
Márton Elek
ca0ea50cba satellite/overlay: remove/deprecate NodeSelectionCache.Disabled
Once uppon a time, at the dawn of the implementation of Storj, when all the nodes are read from the database directly, every time.

After a while --  due to performance reasons -- it has been changed for upload and download: where all the nodes are read for a short period of time, and used from memory.

This is the version which was improved recently to support advanced node selections using placement.

But stil we have an old configuration value `service.config.NodeSelectionCache.Disabled`, and the db based implementation: `service.FindStorageNodesWithPreferences(ctx, req, &service.config.Node)`.

For safety, we need to remove this option, to make sure that we use the cache, which has the advanced features.

This patch was supposed to be a very small one (just removing a method and a config: https://review.dev.storj.io/c/storj/storj/+/11074/1/satellite/overlay/service.go), but it turned out that we need to update a lot of unit tests.

These unit tests used the old implementation (which is not used in production any more).

The tests which used both implementation are just updated to use only the new one
The tests which used only the old implementation are refactored (but keeping the test cases).
Using real unit tests (without DB, working on OSX, fast)

Closes https://github.com/storj/storj/issues/6217

Change-Id: I023f92c7e34235665cf8474513e67b2fcc4763eb
2023-08-31 09:46:29 +00:00
Jeremy Wharton
00194f54a2 web/satellite: show limit notifications to paid tier users
This change causes paid tier users to see notifications in the project
dashboard when their usage is approaching or has reached their maximum
or custom usage limits.

Change-Id: I7b68fcdd7d62797b6b26869e109cfb0b193fdddb
2023-08-31 07:31:09 +00:00
Wilfred Asomani
31ec421299 satellite/db: optimize project usage query
This change addresses an issue where the /charges endpoint will take a
while to respond due to a project having a large number of buckets.
The method and queries involved have been optimized and benchmarks show
a performance improvement.

test name                            old ms/op  new ms/op
Postgres/sum_all_partner_usages          3.659      1.101
Postgres/individual_partner_usages        3.74      1.299
Cockroach/sum_all_partner_usages         7.201      2.872
Cockroach/individual_partner_usages      7.247      2.852

Issue: https://github.com/storj/storj-private/issues/277

Change-Id: Ia5082a2e1c3e91120a9db7b01c18847fe04574fe
2023-08-30 22:08:11 +00:00
Jeremy Wharton
0c9c37875d web/satellite/vuetify-poc: unmock file browser table entries
This change shows real file entries in the file browser table,
replacing the mock data. Sorting, searching, and folder navigation have
been implemented.

Resolves #6199

Change-Id: I7360879d2e26605489c20f9d094c3f231fee49cd
2023-08-30 21:12:55 +00:00
Jeremy Wharton
ebfbbca1be web/satellite/vuetify-poc: pull latest changes from internal repo
The changes as of storj/vuetify-storj@2312936 have been pulled into the
Vuetify project.

Change-Id: Ia45d3d0832727d3ca7b9ee9b3dce5949a79a5d31
2023-08-30 20:34:13 +00:00
Wilfred Asomani
fecaa6a71a web/satellite/vuetify-poc: enable claiming wallets
This change adds the ability to claim STORJ wallets and display token
balance.

Issue: https://github.com/storj/storj/issues/6096

Change-Id: Ifc89b586c0e3ed876905ff0a5b270e718cbb689c
2023-08-30 14:33:35 +00:00
Michal Niewrzal
67371c43bd satellite/metainfo: enable UsePendingObjectsTable by project
This small feature will give us ability to test pending_objects table
without enabling it globally.

Change-Id: I802f45987ad329f94adfc0f02957c802b21d8251
2023-08-30 15:44:29 +02:00
Michal Niewrzal
780c0e0b35 satellite/metainfo: adjust ListPendingObjectStreams to pending_objects
table

New method IteratePendingObjectsByKeyNew is used to provide results for
metainfo.ListPendingObjectStreams. This endpoint is used to list
pending objects with the same object key. In this case to support
both tables (objects, pending_objects) we need to do one query per table
and merge results.

Because existing metainfo protobuf API is missing some fields to have
proper listing cursor we are not able to make ListPendingObjectStreams
correct for returning more than single page. We need to fix it
separately.

With this change also turns out that approach to merge results from
listing objects for ListObjects method was wrong and this change is also
fixing this problem.

Handling both tables will be removed at some point and only
pending_objects will be used to look for results.

Part of https://github.com/storj/storj/issues/6047

Change-Id: I8a88a6f885ad529704e6c032f1d97926123c2909
2023-08-30 13:35:54 +00:00
Wilfred Asomani
6e3da022e0 web/satellite: add pagination to billing history
This change adds pagination to the billing history table. It uses the
new invoice-history endpoint since we only list invoices in this table.

Issue: https://github.com/storj/storj/issues/5479

Change-Id: I192d58503434203808a23a7c18e8d1feb6afc73f
2023-08-30 03:19:31 +00:00
Vitalii
614d213432 web/satellite: fix duplicate satellite selection on login and forgot password pages
Remove duplicate satellite in selection dropdown on login and forgot password pages.

Issue:
https://github.com/storj/storj/issues/6121

Change-Id: I81bd0c2c29c695ed9ed7f0d648311448cdc9bf26
2023-08-30 01:17:14 +00:00
Cameron
a5b1c0432f satellite/console: send unverified user activation link in project invite
When an unverified user is sent a project invitation it contains a
registration link currently. Instead, send an activation link.

github issue: https://github.com/storj/storj/issues/6033

Change-Id: I54b88de8347a2532f7a85372c0c5e4df4bf4eb38
2023-08-29 12:54:09 -04:00
Vitalii
d6e0987dd9 web/satellite: added info message to sharing modals
Add notification explaining that the share will be public to anyone with the link.
Remove the cancel button as it may be interpreted as cancelling or revoking the shared access.
Make the copy link full width block button.

Issue:
https://github.com/storj/storj/issues/6213

Change-Id: I7b9580b3d8135802c36af8e68f46630b499ab110
2023-08-29 12:55:26 +03:00
Wilfred Asomani
6219aba40c satellite/{console,consoleweb,consoleapi}: add new endpoint for paged invoices
This change adds a new endpoint for listing invoices for billing history
This endpoint will replace the billing-history endpoint used on the
front end since were only interested in listing invoices.

Issue: https://github.com/storj/storj/issues/5479

Change-Id: I4730f5dc497245c6730e60b7f9986554479d1d3b
2023-08-28 23:24:12 +00:00
Vitalii
f0829d5961 web/satellite: use stripe as ES module
Start using @stripe/stripe-js lib (ES module) instead of regular stripe dependency.
Use strict typing for stripe commands/events.
This lib makes us able to modify stripe input styling in the future.

Change-Id: Iaba4f32a42e87edc85a4fbad82e5107c21bf19b6
2023-08-28 22:45:52 +00:00
Wilfred Asomani
f1e8cdfe3e web/satellite/vuetify-poc: add project usages to billing
This change adds project usage and cost per project to the vuetify app.

Issue: https://github.com/storj/storj/issues/6117

Change-Id: I8921aacb6bb24b41794008100ea6e52deed76b60
2023-08-28 22:05:00 +00:00
Vitalii
4964ca5ffb web/satellite: don't show CTAs on limit cards if user is not project owner
Hide CTAs on limit cards if user is not the owner of selected project.
We do this because user can't update limits if they are not the owner of the project.

Issue:
https://github.com/storj/storj/issues/6214

Change-Id: Ib6b564cd6afc1b4bed08ee9b26108f803f0ffb89
2023-08-28 21:16:42 +00:00
Michal Niewrzal
c010e37374 satellite/metainfo: adjust ListObjects to use pending_objects table
Adjust metainfo.ListObjects method to use IteratePendingObjects to
support new pending_objects table. New method will be used only when
we are listing pending objects.

Because until objects table will be free from pending objects we can
have results in both tables we are merging listing results. This also
means that in some (rare?) cases we may return more results than
specified listing limit. This situation is temporary.

Part of https://github.com/storj/storj/issues/6047

Change-Id: I06389145e5d916c532dfdbd3dcc9ef68ef70e515
2023-08-28 16:22:54 +00:00
Egon Elbre
273ebd61d7 private/apigen/example: make it nicer
Change-Id: I1bd779090a902ed2b99b3993dc7cf61fc250f10f
2023-08-28 15:13:46 +00:00
Ivan Fraixedes
2d8f396eeb private/apigen: Make API base path configurable
Previously the base path for the API was hardcoded to `/api` and the
specified version.

This was not obvious that the generated code was setting that base path
and it was not flexible for serving the API under a different path than
`/api`.

We will likely need to set a different base path if we pretend to serve
the new back office API that we are going to implement alongside the
current admin API until the new back office is fully implemented and
verified that works properly.

This commit also fix add the base path of the endpoints to the
documentation because it was even more confusing for somebody that wants
to use the API having to find out them through looking to the generated
code.

Change-Id: I6efab6b6f3d295129d6f42f7fbba8c2dc19725f4
2023-08-28 14:35:01 +00:00
Cameron
ec42fdae6d web/satellite: add bucket details dialog
issue https://github.com/storj/storj/issues/6115

Change-Id: I4dd3b93bd43f4871cd28ab595bace9e8ce59b7c2
2023-08-28 13:09:03 +00:00
Michal Niewrzal
b26df035f9 satellite/metainfo: tests for new GetBucketLocation method
Change-Id: I809ebab51606aa9e55dff3c40ef2e865caf06924
2023-08-28 11:49:23 +00:00
Artur M. Wolff
37d6df23fa satellite: implement metainfo.GetBucketLocation endpoint
Updates storj/storj-private#408
Updates storj/storj-private#409

Change-Id: Idaaca74b4a5c9c7907d095e0a3a5f29e52843ce6
2023-08-28 13:48:07 +02:00
Márton Elek
5c12a3406d satellite/nodeselection: improve annotation composability
We would like to make it easier to accept multiple annotations.

Examples:
```
country("GB") && annotation(...)
annotated(annotated(X,...),...)
```

Change-Id: I92e622e8b985b314dadddf83b17976c245eb2069
2023-08-28 09:27:04 +00:00
Jeremy Wharton
2cdc1a973f satellite/console: fix project creation race condition
This change fixes an issue where the project limit could be exceeded if
multiple project creation requests were sent sufficiently close to one
another. This could also be used to bypass project name duplication
checking.

Change-Id: I61cde7abaf25dedc5601c6870275de9938d7b949
2023-08-25 17:18:34 +00:00
Ivan Fraixedes
d34c1b6825 private/apigen: Generate docs & typescript for example
Generate the documentation and the typescript code for the example of
that we have to generate an API through the API generator.

The JSON Go struct field tags are needed because the typescript
generator require them, otherwise it panics.

The test had to be adjusted according to match the tags so Go consider
the structs the same type, otherwise, it doesn't compile.

Change-Id: I3e4ebff9174885c50ca2058b86b7ec60e025945c
2023-08-25 12:30:21 +00:00
Moby von Briesen
d76f059c55 private/apigen: Update generated doc links
Make the link more human-friendly - to contain the text of the group and
endpoint names.

Also link back to list of endpoints from each endpoint.

Change-Id: Ia3e2ebe20b58b5f60ecefe9d35fb8fd90dd4b4d7
2023-08-25 11:51:57 +00:00
Jeremy Wharton
61fe95c44a web/satellite/vuetify-poc: update access grant creation dialog
This change updates the access grant creation dialog to align with our
new designs. It also fixes an issue that would occur if the web worker
was initialized after the dialog was mounted.

Resolves #6169

Change-Id: Ic557766e6fcf57cc79c72e670a0e83c7eb2834ba
2023-08-25 09:11:58 +00:00
Wilfred Asomani
e44365d265 web/satellite: use fontsource for font loading
This change updates the vuetify app and the production app to load
fonts using the fontsource package.

Issue: https://github.com/storj/storj/issues/6200

Change-Id: I4b91a4d0dfcfc42f9f71ac03b31d1ef74c53e15d
2023-08-24 23:08:41 +00:00
Wilfred Asomani
36f8eeb272 web/satellite: fix broken preview link
This change fixes an issue where the gallery view will use wrong links
display object previews because they were encoded multiple times.

Issue: https://github.com/storj/customer-issues/issues/961

Change-Id: I498878e9beb927b812e40d4c7e5ae812cfa3554c
2023-08-24 22:30:03 +00:00
Jeremy Wharton
84e75d5994 satellite/satellitedb: remove subquery AOST in console db cleanup funcs
This change fixes an issue where the console DB cleanup chore was never
able to run when using a Cockroach database implementation because of
an inappropriate AS OF SYSTEM TIME clause in the relevant methods.

Resolves #6197

Change-Id: I8456b6df2128678e0eebeb416eb1a955cc9bd706
2023-08-24 21:51:55 +00:00
Wilfred Asomani
fe9f69a757 satellite/{consoleweb,consoleapi}: add cross user api tests
This change adds tests to ensure critical endpoints are not able to be
called by users for other users. It asserts that if cases like that
do happen, a 401 response will be sent.

Issue: https://github.com/storj/storj-private/issues/407

Change-Id: I70097a80f691a7d0fcb0bc5dbce8291144177720
2023-08-24 20:16:20 +00:00
Moby von Briesen
5c155752d2 satellite/analytics: Update lifecyclestage for personal users
Personal users, like business users, should now be classified with
a lifecycle stage of PQL ("product qualified lead") instead of "other"

Change-Id: Iff5139043da1c8e75559302320ff9ca43ea956e5
2023-08-24 09:47:26 -04:00
Jeremy Wharton
0070cd322a web/satellite/vuetify-poc: fix bucket deletion dialog
This change fixes an issue where the bucket deletion dialog wasn't
functional due to an invalid property value. This change also fixes an
issue where access grant worker errors were reported as being caused by
the bucket deletion dialog even when it wasn't open.

Change-Id: If2c2713857c4cc5c3c7ae60e431f5034e78c4c5f
2023-08-24 09:53:03 +00:00
Moby von Briesen
7fbabbcf16 private/apigen: Add list of endpoints with links
This change makes it easier for someone reading the documentation to see
a full list of supported endpoints, and have direct links to the
details.

Change-Id: I46e2f809cfa2760845898eaa3d99db9066d435ef
2023-08-24 02:45:46 +00:00
Sean Harvey
a0c69568b5 Makefile: add sha256sums checksum file with release binaries
Change-Id: I518e075445288f238104b801f89837e2d8d0d781
2023-08-23 20:43:12 +00:00
Moby von Briesen
5340a351b7 satellite/.../consoleapi/gen: Update README
Remove outdated information from the generated API readme, and add a
link to the generated documentation.

Change-Id: Icc098c81f235464344895d2195444044831aac63
2023-08-23 19:49:35 +00:00
Ivan Fraixedes
b0d072270b private/apigen: Add comment about panics
Add a comment in 2 methods about they panic as it's specified for the
`MustWriteDocs`.

Change-Id: I15d5fa420af37318d828bc633bed62988f8b207b
2023-08-23 18:50:37 +00:00
Michal Niewrzal
95a5cfe647 satellite/buckets: handle bucket exists better
In some rare cases when two entities are trying to create the same
bucket at the same time it's possible that we will return internal
error instead of `bucket already exists`. It's because we are not
handling correctly DB error about constraint error. This change checks
if while inserting bucket into DB we got constraint error and propagate
correct error to metainfo API.

Change-Id: Ie6fd2c943b864b4ea7d71e4a162e74dc3510e386
2023-08-23 14:40:31 +00:00
Márton Elek
84ea80c1fd satellite/repair/checker: respect autoExcludeSubnet anntation in checker rangedloop
This patch is a oneliner: rangedloop checker should check the subnets only if it's not turned off with placement annotation.
(see in satellite/repair/checker/observer.go).

But I didn't find any unit test to cover that part, so I had to write one, and I prefered to write it as a unit test not an integration test, which requires a mock repair queue (observer_unit_test.go mock.go).

Because it's small change, I also included a small change: creating a elper method to check if AutoExcludeSubnet annotation is defined

Change-Id: I2666b937074ab57f603b356408ef108cd55bd6fd
2023-08-23 13:45:09 +00:00
Márton Elek
4ccce11893
satellite/overlay: improve realistic placement rule test
10 --> node tag inclusion in raw format
11 --> same, but using same subnet is enabled
12 --> same as 11 but with US restrictions

Change-Id: I20792689e0caf5fe190f566a770d70c3b3824793
2023-08-23 13:56:35 +02:00
Jeremy Wharton
973b54365e web/satellite/vuetify-poc: add project creation dialog
This change implements a dialog box through which users can create
projects.

Resolves #6172

Change-Id: I99311f43ef49dc04bb852a6736771b42f26a3276
2023-08-22 19:45:35 +00:00
Moby von Briesen
b66fc6dcdb web/satellite: Update build script to build vuetify app
Also copy the necessary vuetify dist directory in the `Earthfile` so
that this app can be easily deployed to staging environments.

Change-Id: I8d91c52bb8f7c31fc3764efe60a071e21b399b46
2023-08-22 13:55:10 +00:00
Wilfred Asomani
516241e406 cmd,satellite: remove Graphql code and dependencies
This change removes unused GraphQL code. It also updates storj sim code
to use the GraphQL replacement HTTP endpoints and removes the GraphQL
dependency.

Issue: https://github.com/storj/storj/issues/6142

Change-Id: Ie502553706c4b1282cd883a9275ea7332b8fc92d
2023-08-22 12:23:14 +00:00
Wilfred Asomani
d10ce19f50 web/satellite: remove Graphql dependency
This change removes unused GraphQL code and dependencies.

Issue: https://github.com/storj/storj/issues/6142

Change-Id: Ib2346c7a034f26b46af1bc6113e007b6adcbdd51
2023-08-21 22:17:41 +00:00
Paul Willoughby
fdd4be80bf satellite/metainfo: increase default MaxEncryptedObjectKeyLength
Allow a longer encrypted key length to reduce 'key length is too big'
errors in gateway-mt.  Gateway is enforcing an unencrypted key length
of 1024 bytes but when encrypted some keys are exceeding the current
limit.

Updates https://github.com/storj/gateway-mt/issues/335

Change-Id: I38a0fbb0843fd782aeadca85f9a202821421b5a2
2023-08-21 14:03:16 -06:00
Wilfred Asomani
9cf9721abe web/satellite: use create project http endpoint
This change uses the new POST projects endpoint in place of the GraphQL
createProject query.

Issue: https://github.com/storj/storj/issues/6195

Change-Id: I1776b8b0e8656d2f5fa4219df020615bcc0f2543
2023-08-21 17:30:38 +00:00
Jeremy Wharton
7f9317aa48 satellite/console: fix account creation race condition
This change fixes an issue where multiple unverified users with the
same email address could be created if registration requests were
sent sufficiently close to one another.

Resolves #6156

Change-Id: If8b1a145bcab842ace718119183de59947430463
2023-08-21 16:52:11 +00:00
Márton Elek
b2780b028d satellite/gracefulexit: use placement when gracefulexit pick new nodes
It's quite straightforward change, and AFAIK graceful exit will be decommissioned very soon.

Therefore I didn't create big unit tests, yet. But I can be convinced to invest more time.

Change-Id: Ia588e516d7af5171fa47f9bab100edd3bf2b2cf9
2023-08-21 15:40:05 +00:00
Michal Niewrzal
005fb19a7b satellite/metabase: adjust BucketEmpty to use pending_objects table
Extends metabase.BucketEmpty logic to check also pending_objects
table for any entry.

https://github.com/storj/storj/issues/6057

Change-Id: Ia26c272de24a983b308a0b692e6bd5800487eb98
2023-08-21 15:01:59 +00:00
Michal Niewrzal
16588033fd satellite/metabase: delete bucket deletes also from pending_objects
While deleting bucket we need also to delete pending objects from
pending_objects table.

Part of https://github.com/storj/storj/issues/6048

Change-Id: Icc83eaecf8388704e0b6329c397e8028debcf672
2023-08-21 14:05:13 +00:00
Jeremy Wharton
ffa50f0758 web/satellite/vuetify-poc: add bucket sharing option to buckets table
This change allows buckets to be shared from the Buckets page through
a dropdown menu in the buckets table.

Resolves #6116

Change-Id: I92055a3ea174d786f2ef960124aafcbd3b2139c4
2023-08-21 11:26:40 +00:00
Michal Niewrzal
ae2cba1d23 satellite/metabase: add IteratePendingObjectsByKeyNew method
New metabase method IteratePendingObjectsByKeyNew to iterate
over entries in pending_objects table with the same object key.
Implementation and tests are mostly copy of code for
IteratePendingObjectsByKey. Main difference is that pending_objects
table have StreamID column part of primary key instead Version.

Method will be used to support new table in
metainfo.ListPendingObjectStreams request.

After full transition to pending_objects table we should remove 'New'
suffix from methods names.

Part of https://github.com/storj/storj/issues/6047

Change-Id: Ifc1ecbc534f8510fbd70c4ec676cf2bf8abb94cb
2023-08-21 08:08:03 +00:00
Michal Niewrzal
929dc80091 satellite/metabase: add IteratePendingObjects method
New metabase method IteratePendingObjects to iterate over entries in
pending_objects table. Implementation and tests are mostly copy of
code that we are using to iterate over objects table. Main difference
is that pending_objects table have StreamID column part of primary
key instead Version. Also structure of pending object is smaller
than the one from object table but it's a detail.

Method will be used to support new table in metainfo.ListObjects
request.

Next step will be to port rest of iterator implementation to support
pending_objects table in metainfo.ListPendingObjectStreams.

Part of https://github.com/storj/storj/issues/6047

Change-Id: Ia578182f88840539f3668d4a242953e061eace02
2023-08-21 08:07:16 +00:00
Michal Niewrzal
5a8ef89824 satellite/{metainfo,metabase}: delete from pending_objects table
We are deleting pending objects while aborting multipart upload. We are
using metainfo BeginDeleteObject to do that. This change starts using
DeletePendingObjectNew to delete entry from pending_objects table when
request indicates that object is in this table.

Part of https://github.com/storj/storj/issues/6048

Change-Id: I4478a9c13c8e3db48dc5de3087ef03d1b4c47a5c
2023-08-21 08:06:23 +00:00
Wilfred Asomani
33c0a82fb7 satellite/console: add create project http endpoint
This change adds an HTTP endpoint for creating projects, to be used in
place of the GraphQL version.

Issue: https://github.com/storj/storj/issues/6195

Change-Id: I0377353418df7c152db6a935e99a3ea7ab4ce625
2023-08-21 06:58:03 +00:00
Cameron
6f47e178e2 web/satellite: add "do not share link" to emails
Change-Id: I94da487b622f083d5cf893d6dd90ad30076f6466
2023-08-18 23:16:13 +00:00
Vitalii
d0c3a59f44 web/satellite: differentiate password and passphrase autocomplete inputs
Added unique autocomplete values for password and passphrase inputs to make browser native autocomplete feature work correctly.

Issue:
https://github.com/storj/storj/issues/6151

Change-Id: I38e1bfed46beb47b3be1a620ce2a4996359feafc
2023-08-18 17:29:20 +00:00
Wilfred Asomani
f46280c93b web/satellite/vuetify-poc: add open bucket passphrase
This change requires a passphrase before opening a bucket and after
opening the objects page without going through the buckets page. It also shows if there are
objects stored with different passphrase.

Issues: https://github.com/storj/storj/issues/6100
https://github.com/storj/storj/issues/6110

Change-Id: Ica509fd6db968ef9c232a05b3ee39e0ab5f746e1
2023-08-18 10:54:05 +00:00
Jeremy Wharton
2b278bb05f web/satellite/vuetify-poc: unmock total cost, token balance, coupon
This change replaces mock data for the total cost, STORJ token balance,
and coupon information in the Vuetify project's billing overview with
real data.

Resolves #6095

Change-Id: Iad1c5141065f8761d7d13140d42871018fa9aca4
2023-08-18 10:14:57 +00:00
Márton Elek
f0afe0d2ea satelite/repairer: ignore declumping when subnet filtering is turned off with filter annotation
+ restoring the functionality of repairer.doPlacementCheck

Change-Id: I75521f2da280758345face07eeea661765717318
2023-08-18 09:35:38 +00:00
Márton Elek
5522423871 satellite/nodeselection: remove AutoExcludeSubnet filter
It's statefull, therefore it can hit naive users. (NodeFilters couldn't be reused for more than one iterations).

But looks like we don't need it, as `SelectBySubnet` doest the same job.

Change-Id: Ie85b7f9c2bd9a47293f4e3b359f8b619215c7649
2023-08-18 08:31:00 +00:00
Jeremy Wharton
db13e3ef15 web/satellite: warn when indenting with tabs
This change adds a rule to the ESLint config to warn when tabs are used
instead of spaces for indentation.

Change-Id: I068d559211e0f9e40abaea86de839a409b405674
2023-08-17 20:45:47 +00:00
Jeremy Wharton
c706df5218 web/satellite/vuetify-poc: make project invite titles unclickable
Users with a pending invitation for a project could be redirected to
that project's dashboard if its title in the projects table was
clicked. This type of redirection is only intended for projects that
the user is a member of, as navigating into any other project will send
the user back to the all projects dashboard. This change fixes this
issue, making invitation titles in the projects table unclickable.

Change-Id: I6ef6a72d92e8eb576d49d6268373af340116ee2f
2023-08-17 20:07:16 +00:00
Wilfred Asomani
ea38b9f78e web/satellite/vuetify-poc: fix vuetify form reloads
This change fixes an issue where the vuetify app will reload when
"enter" is pressed in a form. It also adds a success notification on
successfully enabling 2FA.

Change-Id: I23a66122dce4f1bd2ff21a5cd6ae0f1b22be9395
2023-08-17 19:28:11 +00:00
Jeremy Wharton
957d8d6ca0 web/satellite/vuetify-poc: add bucket deletion option to buckets table
This change allows buckets to be deleted from the Buckets page through
a dropdown menu in the buckets table. Before deletion, users are
prompted to confirm their decision in a dialog box.

Revoles #6114

Change-Id: Ie3a8bfbf94a29c4bc67b4dacbace6abe7e1bc2eb
2023-08-17 18:48:54 +00:00
Jeremy Wharton
f2e607c70f web/satellite/vuetify-poc: fix project navigation sidebar paths
This change resolves an issue where the buttons in the navigation
sidebar would not navigate to the correct page when clicked if the
current path was not a sibling of the intended page's path.

Change-Id: I400aa05954f532eabbad98f9fa36f594f08a6c10
2023-08-17 16:52:31 +00:00
Márton Elek
b218002752 satellite/overlay/placement: improve placement configurability with &&, placement, region and ! support in country
This patch makes it easier to configure existing placement rules only with string.

 1. placement(n) rule can be used to reuse earlier definitions
 2 .&& can be used in addition to all(n1,n2)
 3. country(c) accepts exclusions (like '!RU'), regions ('EU','EEA'), all and none

See the 'full example' unit test, which uses all of these, in a realistic example.

https://github.com/storj/storj/issues/6126

Change-Id: Ica76f016ebd002eb7ea8103d4258bacd6a6d77bf
2023-08-17 16:12:53 +00:00
Egon Elbre
b5e1e5a9e2 private/apigen: use typescript in docs markdown
Fixes https://github.com/storj/storj/issues/6187

Change-Id: I34016513f29a9538343f0909951ab1a4605bb585
2023-08-17 15:33:21 +00:00
Vitalii
5abed63f53 web/satellite: update estimated charges info message
Updated message to be more clear, and changed "estimated charges" to
"estimated usage". This should make this card in the billing UI less
confusing.
Updated styling.

Issue:
https://github.com/storj/storj/issues/6150

Change-Id: I5de4d72dfd437d4f6bda882f5b779b2217e84f98
2023-08-16 15:11:59 -04:00
Márton Elek
9ddc8b4ca3 satellite/repair: piecescheck.OutOfPlacementPiecesSet should not contain offline nodes
When we check the availability of the pieces, we do:

```
result.NumUnhealthyRetrievable = len(result.ClumpedPiecesSet) + len(result.OutOfPlacementPiecesSet)
// + some magic if there are overlaps between them
numHealthy := len(pieces) - len(piecesCheck.MissingPiecesSet) - piecesCheck.NumUnhealthyRetrievable
```

This works only if OutOfPlacementPieceSet doesn't contain the offline nodes (which are already included in MissingPieceSet).

But `result.OutOfPlacementPieces.Set` should include all the nodes (even offline), as in case of lucky conditions, we are able to remove those pieces from DB.

The solution is to remove all offline nodes from `NumUnhealthyRetrievable`.

Change-Id: I90baa0396352dd040e1e1516314b3271f8712034
2023-08-16 17:35:10 +00:00
Jeremy Wharton
80186ecc67 web/satellite/vuetify-poc: add project settings page
This change implements the Project Settings page in the Vuetify
project. It allows users to view and change a project's name,
description, storage limit, and bandwidth limit.

Resolves #6176

Change-Id: Ibcc56d2bb7c71e818390e69eec197508e3551803
2023-08-16 11:55:01 -05:00
Márton Elek
de7aabc8c9 satellite/{repair,rangedloop,overlay}: fix node tag placement selection for repair
This patch fixes the node tag based placement of rangedloop/repairchecker + repair process.

The main change is just adding the node tags for Reliable and KnownReliabel database calls + adding new tests to prove, it works.

https://github.com/storj/storj/issues/6126

Change-Id: I245d654a18c1d61b2c72df49afa0718d0de76da1
2023-08-16 15:45:41 +00:00
Egon Elbre
a3067b7b3b storagenode/monitor: ignore shutdown errors
This fixes some flakyness in tests.

Change-Id: I535232c5d80827d6d72c73d61134f3b2806b5db9
2023-08-16 11:53:58 +00:00
Jeremy Wharton
93ce4a0e49 web/satellite/vuetify-poc: fix offscreen notifications
This change fixes an issue where notifications would display above the
viewport if the page was scrolled down. Now, notifications are fixed to
the top-right corner of the viewport.

Change-Id: I4d55b7d149b889e7b41e2f245ff8547e998877fc
2023-08-16 10:11:28 +00:00
Wilfred Asomani
d0f5f06159 web/satellite/vuetify-poc: add create bucket dialog
This change adds the ability to create a bucket to the vuetify poc.

Issue: https://github.com/storj/storj/issues/6112

Change-Id: Ib56432e2ad09c6673c9903a49537199882fd1cf3
2023-08-16 09:25:33 +00:00
Márton Elek
da08117fcd satellite/~placement: do not ignore placement check for placement=0
There are cases when we would like to override the default placement=0 rule.

For example when we would like to exclude tagged nodes from the selection (by default).

Therefore we couldn't use a shortcut any more, we should always check the placement rules, even if we use placement=0.

TODO: we need to update common, and rename `EveryCountry` to `DefaultPlacement`, just to avoid confusion.

https://github.com/storj/storj/issues/6126

Change-Id: Iba6c655bd623e04351ea7ff91fd741785dc193e4
2023-08-16 07:06:56 +00:00
Jeremy Wharton
baef654197 web/satellite/vuetify-poc: fix visibility of input details
This change resolves an issue that 37a027a introduced wherein each
input component's details area was indiscriminately hidden. Now,
details are only hidden when there is no content to be displayed.

References #6170

Change-Id: I8ccbef3f64a54c400a2a2e9222618eb5015c8dd8
2023-08-15 19:45:53 +00:00
Márton Elek
c08792f066 satellite/overlay: implement an exclude filter for placement configuration
https://github.com/storj/storj/issues/6126

Change-Id: I05215b5d46bec958001cc020edf1fa97b00d3299
2023-08-15 17:29:29 +00:00
Márton Elek
0e17b1018c satellite/{nodeselection,overlay}: support annotations on node filters
Change-Id: I844d8a25042750aae189175842113e2f052d5b17
2023-08-15 16:49:57 +00:00
Jeff Wendling
b70fb2f87f cmd/uplink: fix progress bar crash
the progress bar was being set to inconsistent lengths
multiple times, causing a crash. this fixes that by
only setting the progress bar length once to the length
of the full object. it avoids a round trip by doing so
only after it has gotten the first read handle from the
source, so the length information is cached.

Change-Id: I112d7c79016e54ba3794e96c6174cc01b8baedb4
2023-08-15 13:10:03 +00:00
Egon Elbre
d701f9f081 Makefile: use storjlabs/ci:slim
We moved the slim build to the same image name and used the tag
for distinguishing the different builds.

Change-Id: I82e235ba4db6a0b76b1edccab3b13d006c6c9328
2023-08-15 09:49:27 +00:00
Jeff Wendling
1cbad0fcab cmd/uplink: add back parallelism
for very large machines (>10Gbit) it is still useful
to have parallelism for uploads because we're actually
bound by getting new pieces from the satellite, so doing
that in parallel provides a big win.

this change adds back that flag to exist for uploads, and
removes the backwards compatibility code for the flag with
the maximum-concurrent-pieces as they are now independent.

the upload code parallelism story is now this:

    - each object is a transfer
    - each transfer happens in N parts (size dynamically
      chosen to avoid having >10000 parts)
    - each part can happen in parallel up to the limit
      specified
    - each parallel part can have up to the limit of
      max concurrent pieces and segments

this change also changes some defaults to be better.

    - the connection pool capacity now takes into acount
      transfers, parallelism and max concurrent pieces
    - the default smallest part size is 1GiB to allow the
      new upload code path to upload multiple segments

Change-Id: Iff6709ae73425fbc2858ed360faa2d3ece297c2d
2023-08-14 20:28:58 -04:00
Jeremy Wharton
03690daa35 web/satellite/vuetify-poc: add session timeout and refresh
This change allows sessions within the Vuetify project to be refreshed.
In addition, dialogs appear to inform the user when a session is about
to expire and when it has expired.

Resolves #6147

Change-Id: I53d5508825aa9992e4fed8ce7b957d949ff28e2d
2023-08-14 16:16:51 +00:00
littleskunk
792bb113bc
satellite/console: Enable gallery view and limits area by default (#6177) 2023-08-14 17:05:19 +02:00
Moby von Briesen
b9206b1844 satellite/console: support hosting Vuetify POC on subdomain
This change allows you to host the vuetify app on <x>.example.com where
the main app is hosted on example.com. A configuration is added to
specify an exact subdomain for cookies. For example, if my production
app is hosted on us1.storj.io and my vuetify app is hosted on
vuetify.us1.storj.io, the cookie domain should be set to ".us1.storj.io"
so that any authentication cookie is accessible to lower-level
subdomains.

Since the vuetify app does not currently support login/signup on its
own, it is still required to first login to the main satellite UI, then
navigate to the Vuetify app after the session cookie is set.

If the "vuetifypoc" prefix is not desirable when using subdomain hosting
for vuetify, the VITE_VUETIFY_PREFIX variable can be modified in
web/satellite/.env before running `npm run build-vuetify`. For now, we
should keep this prefix because it makes developing on the vuetify app
significantly easier if subdomains are not being used.

Issue: https://github.com/storj/storj/issues/6144

Change-Id: Iba1a5737892c8ee8f38148a17b94e3222f8798e6
2023-08-14 13:15:41 +00:00
Márton Elek
ceb7b7375c satellite/geoip: exclude nodes with represented_contry from geofencing
Change-Id: I80b8e5d98f46559b158a26c47fff0586b97aff79
2023-08-14 12:36:33 +00:00
Wilfred Asomani
0fd7f2958f web/satellite/vuetify-poc: add functionality to add credit cards
This implements functionality to add cards to users' accounts, to the
vuetify POC.

Issue: https://github.com/storj/storj/issues/6097

Change-Id: Ie56e85e74fd44de6839e6a985877843b730a3f5f
2023-08-14 09:45:53 +00:00
Cameron
df60380793 web/satellite: use project members delete http endpoint
issue: https://github.com/storj/storj/issues/6136

Change-Id: I717755721dc29714ad161fd311f7226a69acc163
2023-08-11 13:48:35 -04:00
Cameron
969599b60b satellite/console: delete project members endpoint
This commit adds a new endpoint on the console api to delete project
members and invitations.

issue: https://github.com/storj/storj/issues/6136

Change-Id: I980bb97afd1ed2ed8f0f27cc2e8dc1d80d7eef05
2023-08-11 13:48:33 -04:00
Vitalii
d7d196fe61 web/satellite: small vuetify fixes
Removed selection/checkboxes on team table.
Made search bars on searchable tables clearable.
Navbar toggle works on small screen sizes.
Removed notifications section from the settings page.

Issue:
https://github.com/storj/storj/issues/6148

Change-Id: I531088cf0db04fd2979d39f92bb4342e0d9551a6
2023-08-11 16:14:39 +00:00
Jeremy Wharton
03b45162d9 web/satellite/vuetify-poc: update alert styling
The custom styling for the VAlert component as of
storj/vuetify-storj@71c509a has been pulled into the Vuetify project.

References #6170

Change-Id: Ia36fd936ddc33c7687bcf7b2cdd6f789d0bc0a92
2023-08-11 10:17:16 -05:00
Moby von Briesen
dbc7443c9d web/satellite: Set part size back to 64mb
Reverts this commit to make part size dynamic:
a9d979e4d7

There are issues with client-side memory usage using the dynamic sizing.

Change-Id: Iba18af1e424622b7b3bf57413322332d8e1aa63a
2023-08-11 12:53:10 +00:00
Vitalii
8b0d25cde1 web/satellite/vuetify: speed up build commands
Fixed the way we use vuetify settings. Seems like the problem was related to SASS variables.
Partially used this approach https://github.com/vuetifyjs/vuetify/issues/8169#issuecomment-1495643012

Also, bumped vite and vuetify deps.

New build time for me:
real    0m28.629s
user    0m41.237s
sys     0m3.753s

Issue:
https://github.com/storj/storj/issues/6143

Change-Id: I94f8ee77057b85faf4d1a10bed7b43ba8ea929f1
2023-08-11 12:13:08 +00:00
Jeremy Wharton
75f2152ae3 web/satellite: modularize session timeout code
This change adds a Vue composable and wrapper component for reusing
session timeout code. In the future, the composable will be used to
implement session timeout in the Vuetify project.

References #6147

Change-Id: Ibd049a8a8041007319798ac4187a6ed6487b591f
2023-08-10 20:55:57 -05:00
Vitalii
6c035a70af web/satellite: modify 'add STORJ token' behavior for free-tier users
When a user is in the free tier and attempts to add STORJ from billing screen, open the "upgrade" modal instead of the simpler "add STORJ tokens" modal.
The simpler modal is still used for Pro users.

Issue:
https://github.com/storj/storj/issues/6165

Change-Id: I261d67e1c4d569f7058da828bcb145a64136c231
2023-08-10 19:11:05 +00:00
Egon Elbre
0335697664 satellite/accounting/live: ensure we don't panic when we get nil
Change-Id: I99b6b94d37a9856e8a705679d117b42d16326e81
2023-08-10 19:41:57 +03:00
Egon Elbre
1f261bcc70 go.mod: bump lang to 1.19 and common
Change-Id: I8d91f97d786456da29ebe89a78412c50efbb8ccc
2023-08-10 18:41:15 +03:00
Jess Stingray
6f01a81648
Downgrade "context canceled" errors to Info instead of Error (#6166) 2023-08-10 09:33:49 +02:00
Vitalii
e226606ce8 web/satellite: updates for large file upload
Universal "large file notification" removed.
Threshold for user-triggered large file notification is 5gb instead of 1gb.
Large file notification appears as "info" instead of "warning".
Display large file notification on failed uploads >1gb.

Issue:
https://github.com/storj/storj/issues/6149

Change-Id: I6ae6ba50e45c65f058cbb91a5f0fbda79a49a812
2023-08-09 23:39:15 +00:00
Wilfred Asomani
6b2fb9dfc4 web/satellite: use force delete bucket
This change modifies delete bucket requests to optin for force delete
using the x-minio-force-delete header. This solves an issue where
buckets deletes will be slow for buckets that have many objects. This
change is to make the UI emulate uplink rb --force since it has better
performance.

Issue: https://github.com/storj/customer-issues/issues/930

Change-Id: I0a74c1a201e74b07eb30b917adf78ef865ce2003
2023-08-09 23:05:50 +00:00
Vitalii
7d149dca0f web/satellite: use delete API keys http endpoint
This change uses the new /delete-by-ids endpoint in place of the GraphQL query.

Issue:
https://github.com/storj/storj/issues/6140

Change-Id: Ia78016e68fca296367e650b7a919130e662ee8f6
2023-08-09 22:32:47 +00:00
Wilfred Asomani
a88711319c web/satellite: use update project http endpoint
This change uses the update project HTTP endpoint in place of the
GraphQL alternative.

Issue: https://github.com/storj/storj/issues/6134

Change-Id: I7d8203c20d8c0c1dcdddb7d1aaf472fc66568a3d
2023-08-09 16:41:47 +00:00
Wilfred Asomani
5bdb7bc3f0 satellite/console: add update project http endpoint
This change adds an endpoint update projects, to be used in place of
the graphql alternative.

Issue: https://github.com/storj/storj/issues/6134

Change-Id: I26c04f4400f71721cbddb7f64405e6c9b78edb4d
2023-08-09 16:07:51 +00:00
Jeremy Wharton
81163321ad web/satellite/vuetify-poc: remove project description from nav button
This change removes the project description from the subtitle of the
topmost item in the project navigation area, replacing it with the
project name and inserting "Project" where the name used to be.

References #6170

Change-Id: I07a04ebba54828df41f4d241aa69ec6b3d9e0cef
2023-08-09 14:35:13 +00:00
Jeremy Wharton
37a027af67 web/satellite/vuetify-poc: hide input message areas with no content
The message area beneath input components has been hidden by default so
that it doesn't occupy space when it isn't needed.

References #6170

Change-Id: I16b078cddb07708169b96e53db283aedb290f9f4
2023-08-09 14:01:52 +00:00
Jeremy Wharton
eace6e37b8 web/satellite/vuetify-poc: fix dialog close button position
This change fixes the position of the close button in dialogs where
there was no spacing between the button and the edges of the dialog
header.

References #6170

Change-Id: Id3f798953319c1703d7bb6868757aecd02ec09f5
2023-08-09 13:28:59 +00:00
Cameron
c931234e46 web/satellite: use project members http endpoint instead of graphql
issue: https://github.com/storj/storj/issues/6137

Change-Id: I1f9b0d74fa4796c4dbee3b80618e87583b0f3f35
2023-08-09 12:56:01 +00:00
Jeremy Wharton
ea94bc7f6d web/satellite/vuetify-poc: add access grant creation flow
This change implements the access grant creation flow in the Vuetify
project.

Resolves #6061

Change-Id: I233088cbfcfe458936410899531389e290f276d4
2023-08-09 12:23:24 +00:00
Egon Elbre
dc41978743 all: fix golangci failures
Change-Id: I07421388d53c837e35a4727cead26fc21c324d04
2023-08-09 11:44:44 +03:00
Clement Sam
9e3d54fec4 satellite/admin: extend API to allow setting and deleting account level geofence
Issue: https://github.com/storj/storj-private/issues/357
Change-Id: I04589e18214e7090ccd686fd531066d942afa6ed
2023-08-09 03:34:37 +00:00
Jeremy Wharton
9e00d495c4 web/satellite: use get buckets http endpoint instead of graphql
This change causes the satellite frontend to use the HTTP endpoint for
retrieving bucket usage totals rather than its GraphQL counterpart.

Resolves #6141

Change-Id: I9a42080c8344aa617a910a8782dc61101a6734c8
2023-08-08 21:59:25 +00:00
Jeremy Wharton
a00ec7af40 satellite/console: create http endpoint for getting bucket usage totals
This change introduces an HTTP endpoint for retrieving bucket usage
totals. In the future, this will replace its GraphQL counterpart.

References #6141

Change-Id: Ic6a0069a7e58b90dc2b6c55f164393f036c6acf4
2023-08-08 21:26:17 +00:00
Cameron
683119b835 satellite/console: get project members endpoint
This commit adds a new endpoint on the satellite console api to get
project members and invitations.

issue: https://github.com/storj/storj/issues/6137

Change-Id: I66cb064eeaffb1c34878462b3e6b3be8f3629f4e
2023-08-08 14:10:29 -04:00
Michal Niewrzal
887209bc24 satellite/metabase: fix CommitObject query for postgres
Query was not working for postgres because types were not correctly set.

Change-Id: Ic898aa37b71a4754e4ebe82085f3563fc954616a
2023-08-08 15:31:44 +00:00
Wilfred Asomani
55b23d2bdb web/satellite: use get owned projects http endpoint
This change uses the new /projects/paged endpoint to get user's owned
projects in place of the OwnedProjects graphQL query.

Issue: https://github.com/storj/storj/issues/6135

Change-Id: I1e8dfdefdc7de51595637577f7d8bdce8e969652
2023-08-08 14:03:18 +00:00
Wilfred Asomani
34e1caa55a satellite: add get user paged projects http endpoint
This change adds an endpoint to get a user's projects, similar to
the OwnedProjects GraphQL query.
The console.ProjectInfo struct has been renamed to UpsertProjectInfo
to more accurately reflect its use.

Issue: https://github.com/storj/storj/issues/6135

Change-Id: I802fe4694a5cc75a9df2b565476f6e6f473431d4
2023-08-08 14:02:53 +00:00
Wilfred Asomani
b21041d6f9 web/satellite/vuetify: add recovery MFA codes dialog
Added regenerate MFA codes dialog and functionality.

Issue: https://github.com/storj/storj/issues/6091

Change-Id: I5139e83077adb6deea1ad57c0ebe4e84a88e770c
2023-08-08 13:09:01 +00:00
Michal Niewrzal
7be844351d satellite/metainfo: remove ServerSideCopyDuplicateMetadata
https://github.com/storj/storj/issues/5891

Change-Id: Ib5440169107acca6e832c2280e1ad12dfd380f28
2023-08-08 12:15:10 +00:00
Michal Niewrzal
9550b5f4a5 satellite/metabase: drop object deletion code for copy with references
With this change we are removing code responsible for deleting objects
and supporting server side copies created with references. In practice
we are restoring delete queries that we had before server side copy
implementation (with small exception, see bellow).

From deletion queries we are also removing parts with segment metadata
as result because we are not longer sending explicit delete requests to
storage nodes.

https://github.com/storj/storj/issues/5891

Change-Id: Iee4e7a9688cff27a60fb95d60dd233d996f41c85
2023-08-08 11:31:12 +00:00
Michal Niewrzal
03f8ad323d satellite/metabase: remove segment_copies support from ListSegments
We don't need to support segment copies with references anymore.
We migrated to copies where all metadata are copied from original
segment to copy.

https://github.com/storj/storj/issues/5891

Change-Id: Ic91dc21b0386ddf5c51aea45530024cd463e8ba9
2023-08-08 11:21:08 +00:00
Vitalii
6c3300c522 satellite/console: add delete api keys http endpoint
This change adds an endpoint to delete API keys, similar to GraphQL mutation.

Issue:
https://github.com/storj/storj/issues/6140

Change-Id: Ia4a808222a057a199d803d8ea6b944c411a4dc8d
2023-08-08 10:46:54 +00:00
Vitalii
5345eadffa web/satellite: use create API key http endpoint
This change uses the new /create endpoint in place of the GraphQL query.

Issue:
https://github.com/storj/storj/issues/6139

Change-Id: Ic7db9407377b742ab87c887a74a37eaec6a8dbb6
2023-08-08 10:46:25 +00:00
Michal Niewrzal
7f249ab7ca cmd/tools: remove migrate-segment-copies tool
Migration was done. We can remove tool now.

https://github.com/storj/storj/issues/5891

Change-Id: I5d56bad1ac680cd77dabfcf271788e100a6a435b
2023-08-08 10:00:40 +00:00
Michal Niewrzal
a5cbec7b3b satellite/metabase: drop bucket deletion code for copy with references
With this change we are removing code responsible to handle deleting
bucket and supporting server side copies created with references. In practice we are restoring delete queries that we had before server side copy implementation (with small exception, see bellow).

From deletion queries we are also removing parts with segment metadata
as result because we are not longer sending explicit delete requests to
storage nodes.

https://github.com/storj/storj/issues/5891

Change-Id: If866d9f3a1b01e9ebd9b49c4740a6425ba06dd43
2023-08-08 09:07:44 +00:00
Wilfred Asomani
00531fe2b0 web/satellite/vuetify: add disable MFA dialog
Added disable MFA dialog and functionality.

Issue: https://github.com/storj/storj/issues/6091

Change-Id: I24cf863b2a04cc147894ab0640e1e1491acc430d
2023-08-08 07:57:01 +00:00
Vitalii
185ebe3dcf web/satellite/vuetify: added notifications
Added notifications to Vuetify app.
Populated existing functionality with notifications.

Issue:
https://github.com/storj/storj/issues/6087

Change-Id: I8339c372bb32fbf1e0ea136c92383494c129b4b6
2023-08-08 07:14:52 +00:00
Vitalii
f57bc81ce7 satellite/console: add create api key http endpoint
This change adds an endpoint to create new API key, similar to GraphQL mutation.

Issue:
https://github.com/storj/storj/issues/6139

Change-Id: I2b35d680fa8e019666c811ad3bdf16201e3b8946
2023-08-07 23:58:38 +00:00
Vitalii
fa4f5a6ae9 web/satellite: use get project api keys http endpoint
This change uses the new /list-paged endpoint in place of the GraphQL query.

Issue:
https://github.com/storj/storj/issues/6138

Change-Id: I68c35437f3f2e18898783e2fd523cfdfb3f10de3
2023-08-07 18:59:30 +00:00
Vitalii
256bd18120 satellite/console: add get project api keys http endpoint
This change adds an endpoint to get paged project API keys, similar to GraphQL query.

Issue:
https://github.com/storj/storj/issues/6138

Change-Id: I5dea9e4ac61e798cc8a2e56a2755d722c1b66bfa
2023-08-07 18:26:31 +00:00
Vitalii
a4f7f0634d web/satellite/vuetify: added analytics events for currently implemented functionality
Populated already implemented functionality with analytics events.

Issue:
https://github.com/storj/storj/issues/6122

Change-Id: I1e250ca02debc9bedbf78024f74e0dbfa9dfbcb9
2023-08-07 17:52:39 +00:00
Vitalii
e2e437dd95 web/satellite: use analytics pinia module instead of direct API requests
Start using analytics pinia module instead of making direct API requests from components.
We do this to not mix transport layer logic with view logic.

Issue:
https://github.com/storj/storj/issues/6122

Change-Id: Idb1902aec0635df4f0e682ba50bcc97b240ac4a9
2023-08-07 17:18:10 +00:00
Wilfred Asomani
e5bcb8b209 web/satellite: use get user projects http endpoint
This change uses the new /projects endpoint in place of the GraphQL
MyProjects query.

Issue: https://github.com/storj/storj/issues/6132

Change-Id: Ie4ae69dd6def75c4b8c627aaf55c31914cf69ce5
2023-08-07 16:42:41 +00:00
Wilfred Asomani
70f6b60d91 satellite/console: add get user projects http endpoint
This change adds an endpoint to get a user's projects, similar to
the MyProjects GraphQL query.

Issue: https://github.com/storj/storj/issues/6132

Change-Id: I91feb5a1ee8c1231a8a5e6de9b8dc5b256f857c5
2023-08-07 16:05:09 +00:00
Vitalii
d8d3bb5033 web/satellite/vuetify: added update session timeout dialog
Added update session timeout dialog for vuetify app and wired it up with the backend

Issue:
https://github.com/storj/storj/issues/6090

Change-Id: Icfd981c8d24a87b199e5e1eeb599f6369e6ee5c4
2023-08-07 15:31:13 +00:00
paul cannon
6e46a926bb satellite/nodeselection: expand SelectedNode
In the repair subsystem, it is necessary to acquire several extra
properties of nodes that are holding pieces of things or may be
selected to hold pieces. We need to know if a node is 'online' (the
definition of "online" may change somewhat depending on the situation),
if a node is in the process of graceful exit, and whether a node is
suspended. We can't just filter out nodes with all of these properties,
because sometimes we need to know properties about nodes even when the
nodes are suspended or gracefully exiting.

I thought the best way to do this was to add fields to SelectedNode,
and (to avoid any confusion) arrange for the added fields to be
populated wherever SelectedNode is returned, whether or not the new
fields are necessarily going to be used.

If people would rather I use a separate type from SelectedNode, I can do
that instead.

Change-Id: I7804a0e0a15cfe34c8ff47a227175ea5862a4ebc
2023-08-07 12:44:49 +00:00
Márton Elek
0b02a48a10
satellite/nodeselection: SelectBySubnet should use placement filters for all nodes
Current node selection logic (in case of using SelectBySubnet):

 1. selects one subnet randomly
 2. selects one node randomly from the subnet
 3. applies the placement NodeFilters to the node and ignore it, if doesn't match

This logic is wrong:

 1. Imagine that we have a subnet with two DE and one GB nodes.
 2. We would like to select DE nodes
 2. In case of GB node is selected (randomly) in step2, step3 will ignore the subnet, even if there are good (DE) nodes in there.

Change-Id: I7673f52c89b46e0cc7b20a9b74137dc689d6c17e
2023-08-04 10:48:15 +02:00
Michal Niewrzal
03c52f184e satellite/metabase: adjust CommitObject to use pending_objects table
Change is adjusting CommitObject to use `pending_objects` table to
commit object.

Satellite stream id is used to determine if we need to use
`pending_objects` or `objects` table during commit.

General goal is to support both tables until `objects` table will be
free from pending objects.

Part of https://github.com/storj/storj/issues/6046

Change-Id: I2ebe0cd6b446727c98c8e210d4d00504dd0dacb6
2023-08-03 10:21:22 +00:00
Márton Elek
2ed08922d9 satellite/api: configuration option to set additional node tag authorities
Change-Id: Iba387e37cb586ce1378668c99beb3b4db8a9064d
2023-08-03 08:34:02 +00:00
Wilfred Asomani
034542db8f web/satellite/vuetify: add enable MFA dialog
Added enable MFA dialog and functionality.

Issue: https://github.com/storj/storj/issues/6091

Change-Id: Idf2f27937549b9bb709ddcc70ffa4611beea7b78
2023-08-02 17:31:02 +00:00
Michal Niewrzal
f40805763e satellite/metabase: adjust segment commit to use pending_objects table
Change is adjusting CommitSegment to check pending object existence in
`pending_objects` or `objects` table.

Satellite stream id is used to determine if we need to use
`pending_objects` or `objects` table.

General goal is to support both tables until `objects` table will be
free from pending objects. Whenever it will be needed code will be
supporting both tables at once.

Part of https://github.com/storj/storj/issues/6046

Change-Id: I954444a53b4733ae6fc909420573242b02746787
2023-08-02 16:56:25 +00:00
Cameron
fac522d8dd web/satellite: implement vuetify poc project dashboard
issue: https://github.com/storj/storj/issues/6060

Change-Id: I40497a39afacd4787468e1259225ddd75e78295b
2023-08-02 16:22:22 +00:00
Michal Niewrzal
7b2006a883 satellite/metabase: adjust BeginSegment to use pending_objects table
Change is adjusting BeginSegment to check pending object existence in
`pending_objects` or `objects` table.

Satellite stream id is used to determine if we need to use
`pending_objects` or `objects` table.

General goal is to support both tables until `objects` table will be
free from pending objects. Whenever it will be needed code will be
supporting both tables at once.

Part of https://github.com/storj/storj/issues/6046

Change-Id: I08aaa605c23d82695fde352fdbd0a7fd11f46bb5
2023-08-02 15:30:23 +00:00
Michal Niewrzal
cebf255d64 satellite/metabase: adjust BeginObjectNextVersion to use pending_objects
Change is adjusting BeginObjectNextVersion to create pending object in
`pending_objects` or `objects` table depends on configuration. This is
first change to move pending objects from objects table.

General goal is to support both tables until `objects` table will be
free from pending objects. Whenever it will be needed code will be
supporting both tables at once.

To be able to decide if we need to use `pending_objects` table or
`objects` table we extend satellite stream id to keep that information
for later use.

BeginObjectExactVersion will be not adjusted because at the moment it's
used only in tests.

Part of https://github.com/storj/storj/issues/6046

Change-Id: Ibf21965f63cca5e1775469994a29f1fd1261af4e
2023-08-02 14:42:26 +00:00
Jeremy Wharton
391a63f9fa web/satellite: bump Vuetify to v3.3.10
This change updates the Vuetify dependency version so that we may use
new components.

Change-Id: Ib4ae4db3b50c267179503ff866077379d52b6d07
2023-08-02 13:46:20 +00:00
Vitalii
2d2f1b858e web/satellite: show pending transactions during token upgrade flow
Made UI updates to reflect pending token payments during upgrade account flow.
So pending transactions with confirmations count are displayed on Add STORJ Tokens step of upgrade flow.
While this modal is open we make a request to storjscan once in 20 seconds to get recent confirmations count.
When all transactions become confirmed we show success view where a sum of STORJ tokens received is displayed.

Issue:
https://github.com/storj/storj/issues/5978

Change-Id: Icfdc1e5080ed58cea1822cb7d85551ba8064c636
2023-08-02 15:39:25 +03:00
Márton Elek
63c8cfe4c3 satellite/nodeselection: remove CountryCodeExclude
We don't need it any more, as CountryCode uses location.Set, which supports exclusion (`Without`).

Change-Id: Ie311ae19fefa0bc9a0161496af1233ef4a6607df
2023-08-02 09:48:36 +00:00
Márton Elek
f7b39aaed4 satellite/nodeselection: remove stats/size from nodeselection state
stats/size/count is not used by any production code, and it's not required, as we can assert the state with other checks.

real motivation: next commits will make the Selector of the State configurable, therefore we won't have one single Stat, it depends on the request parameters.

(we plan to support both network and id based randomization)

Change-Id: I631828fc0046d2fef5b7a674fc0268a0446e9655
2023-08-01 18:29:41 +00:00
Wilfred Asomani
7c65c0cea5 satellite/{db,console,payments}: unfreeze user with no failed invoices
This change extends the autofreeze chore to go through users who have
been warned/frozen to check if they have no failed invoices. If they do
not, this extension unwarns/unfreezes them.

Issue: https://github.com/storj/storj/issues/6077

Change-Id: I570b1d4b2e29574bd8b9ae37eb2d4fb41d178336
2023-08-01 17:54:39 +00:00
JT Olio
e78658d174 go.mod: use latest maxmind-golang library
Change-Id: I6f06785a096d7d05b175d468482db658b7436a87
2023-08-01 15:22:35 +00:00
Michal Niewrzal
65aa9c11bc satellite/gc/sender: adjust some defaults
Change-Id: I94f5a18bc2d4218e834259b2ba22f3578745c975
2023-08-01 14:27:44 +00:00
Vitalii
59034ca094 web/satellite: upgraded aws-sdk dependencies to resolve vulnerabilities
Upgraded package-lock version to correspond to node v18+.
Upgraded aws-sdk dependencies to resolve vulnerabilities.
Also, fixed typing errors in object browser pinia module.

Change-Id: I35e6e219e66f98ca167ccb4ac57ad07ac99efff1
2023-08-01 13:41:15 +00:00
Vitalii
d40091a3cb web/satellite/vuetify: change name dialog
Added change name dialog for vuetify app and wired it up with backend.

Issue:
https://github.com/storj/storj/issues/6088

Change-Id: I37af595c9e48034d59ead051918bbb4ec5e6ff31
2023-08-01 14:26:16 +03:00
Wilfred Asomani
af69e20cc8 web/satellite: fix errant pagination
This change fixes an issue where changing the size of a page will also
change the current page. It uses a more accurate calculation to
determine if the new size and current page will result in a page index
error.

Issue: https://github.com/storj/storj/issues/6094

Change-Id: Ie62f79191eb34ccd75ccd42b923b8866fe4e4d7f
2023-07-31 22:43:52 +00:00
Vitalii
ceef4b8362 web/satellite/vuetify: added change password dialog
Added change password dialog for vuetify app and wired it up with backend.

Issue:
https://github.com/storj/storj/issues/6089

Change-Id: Ib1dffa947b65c299b278a48ed1491a623895a0bd
2023-07-31 17:03:26 +03:00
Márton Elek
6f002f4220
satellite/overlay: NR placement should exclude nodes without geofencing information
https://github.com/storj/storj-private/issues/378

Change-Id: If2af02083496e5a8eefe27beabb406388ee50644
2023-07-31 09:55:54 +02:00
Vitalii
ae5e742a12 web/satellite: update wording for upgrade account banner
Updated wording because we don't want to dissuade customers from upgrading and using Storj by implying that there is a ceiling to what they can store and egress with Storj.

Issue:
https://github.com/storj/storj/issues/6040

Change-Id: I8a08bea47b698e66e023d1bfcaa5ef09dae79192
2023-07-28 09:46:49 +00:00
Vitalii
62f52c829a web/satellite/vuetify: add charts to project dashboard
Imported chart components from main project to vuetify app project dashboard with slight updates.

Issue:
https://github.com/storj/storj/issues/6071

Change-Id: I51254ea60aab59c7a841784b5dac7d41446e1df4
2023-07-27 23:16:40 +00:00
Vitalii
fcbb37fb66 web/satellite: fix build-watch
Build-watch is broken again since we don't use NODE_ENV variable anymore.
Adjusted code to work with 'mode' setting.

Change-Id: I08b19ef59b39e6b14ce05d7340f032ee8377c49c
2023-07-27 21:36:18 +00:00
Wilfred Asomani
c934974652 satellite/{console,web}: update error handling
This change updates how API errors are handled and sent.

Change-Id: Ia4c71eeb6f2d009a47b59ce77a23f70b8b10f6dc
2023-07-27 21:01:01 +00:00
Vitalii
2c56599ca0 web/satellite: revert uploads queue size
Revert upload parallelism queue size back to 4 segments at a time.
Increasing this number causes some memory issues for some users.

Issue:
https://github.com/storj/storj/issues/6120

Change-Id: Ic5bcd1f13ee625fdc2613ee23d6945c905f03142
2023-07-27 19:57:11 +03:00
Clement Sam
cc12a48c24 satellite/admin: extend admin API to allow setting and deleting geofence for projects
Issue: https://github.com/storj/storj-private/issues/357
Change-Id: Ib59319581641f1f5da71c629143e12f11eb04925
2023-07-27 11:40:26 +00:00
Michal Niewrzal
4cc167a6bd satellite/metabase: use better label for ignoring FTS queries
For some test queries we are using workaround to filter them out from
full table scan detection. To avoid confustion what is this all about
we are changing label to be more descriptive.

Change-Id: I41a744e8faf400e3e8de7e416d8f4242f9093fce
2023-07-26 20:01:38 +00:00
Michal Niewrzal
3d9c217627 satellite/metabase: add pending_objects table
This change adds only schema definition of pending_objects table and
small amount of supporting code which will be useful for testing later.

With this table we would like to achieve two major things:
* simplify `objects` table, before we will start working on object
versioning
* gain performance by removing need to filter `objects` results with `status` column, which is not indexed and we would like to avoid that

https://github.com/storj/storj/issues/6045

Change-Id: I6097ce1c644a8a3dad13185915fe01989ad41d90
2023-07-26 20:00:58 +00:00
Moby von Briesen
28737f5c62 web/satellite: Implement "sign out" in vuetify poc
Implements the "sign out" button in the vuetify app to clear all app
data and navigate to login.

For now, there is a hard refresh after the router navigates to login,
becuase the vuetify poc does not have its own login page to route to.
The hard refresh allows the user to be presented with the main app's
login page after signing out.

Change-Id: Idb1c1e6af8511ea5db6533e46b96b9c15693379f
2023-07-26 13:49:30 -04:00
Moby von Briesen
7487809476 web/satellite: unmock list buckets in vuetify app
This change implements the table on the buckets view of the vuetify app.
Searching and pagination are implemented, but sorting functionality
needs to be added in a separate change - we need to extend the
bucketsStore/backend functionality in order to support sorting.

Resolves https://github.com/storj/storj/issues/6062

Change-Id: I4e92e6facbd7b21f4ec081e41f7e568ddb3cea29
2023-07-26 13:49:30 -04:00
Michal Niewrzal
16c5b3c340 mod: bump uplink to v1.11.0
Change-Id: I88922421f250a261b78227a409f4879ae5221140
2023-07-26 15:01:18 +00:00
Vitalii
6d94d6a681 satellite/{console, accounting}: fix project bandwidth calculation which is shown on project dashboard
Another try to fix calculation of used bandwidth which is displayed on Project Dashboard.
This change sums up allocated-dead traffic for the last 3 days and settled traffic for the period which is earlier than 3 days ago.

Issue:
https://github.com/storj/storj-private/issues/293

Change-Id: I91e652eba69f81bd21e0d053ac170e2b926b3cb4
2023-07-26 06:14:48 +00:00
Vitalii
5db0fd8846 web/satellite: wire up vuetify access page to backend
Added logic to list, sort, search and paginate access grants table for Vuetify POC

Change-Id: I215a9ad4f894b6ac985cb6a3059fdcf007e3d914
2023-07-24 23:17:19 +00:00
Jeremy Wharton
28711e30ad web/satellite/vuetify-poc: add project ID to project-specific routes
A project ID path parameter has been incorporated into project-specific
routes. Previously, project IDs were stored only in local storage,
which made it impossible to bookmark or share links to pages within a
particular project.

Resolves #6076

Change-Id: I4f46f58a95dfc9b67d95fdb1c6a65b50fcafe02b
2023-07-24 22:25:29 +00:00
Jeremy Wharton
5a03e29fca web/satellite/vuetify-poc: unmock all projects dashboard data
The all projects dashboard of the Vuetify project has been updated to
display real project information rather than mock data. Additionally,
projects may now be selected and project invitations replied to through
the all projects dashboard.

Resolves #6036
Resolves #6038

Change-Id: I8aef0dc07c172e2bc8e2f7eb26a3d205bd56067f
2023-07-24 21:39:28 +00:00
Jeremy Wharton
9086078ac7 web/satellite: remove glob patterns from linting commands
The glob patterns in the linting commands have been removed in favor of
a simpler way of expressing which files should be linted.

Change-Id: I150a01725642b4bc445e2e157ddf86e50e3911a2
2023-07-24 10:33:07 -05:00
Michal Niewrzal
73a279235a satellite/accounting/live: get project totals in batches
After infrastructure changes redis instance is not neccessay close
to core instance (where tally is calculated) and round trips to get
data from redis can be very costly. From less than hour calculation can take few hours for larger satellite.

This change combines 'segment' and 'storage' usage requests into
batches to reduce latency impact on tally calculation.

https://github.com/storj/storj/issues/5800

Change-Id: I87e57ec09e88fd167060a4ed51dc8b0274a095c5
2023-07-24 14:13:04 +00:00
Jeremy Wharton
f30e0986b6 web/satellite/vuetify-poc: fix app store name conflict
The name of the Vuetify Pinia app store has been renamed so that it
does not conflict with the regular app store. Previously, whichever
store was created first would override the other due to them having
identical names.

Change-Id: I11cb3b70b7385320accd30644cb3ad6cfad7d00e
2023-07-22 20:51:50 -05:00
Cameron
b16c8ba2e4 web/satellite: add indeterminate progress bar
Display indeterminate progress bars in upload modal if progress is less
than 1.

Change-Id: Icdad8e59914985f3ed8fd25dd01dba7e9ff88cf0
2023-07-21 18:11:18 -04:00
Wilfred Asomani
8ed4c573db satellite/admin: add endpoint to unwarn user
This change enables the admin UI to remove the warning status of users.

resolves: storj-private/issues/342

Change-Id: Ib960ffb33fdabc045884ce7fa2c55c3553db0fb0
2023-07-21 17:10:09 +00:00
Vitalii
4ac8320f3c web/satellite: update wording for upgrade account token flow
Update wording to reflect latest auto upgrade account flow.

new wording:
Send more than $10 in STORJ Tokens to the following deposit address to upgrade to a Pro account.
Your account will be upgraded after your transaction receives 15 confirmations.
If your account is not automatically upgraded, please fill out this limit increase request form.

Issue:
https://github.com/storj/storj-private/issues/367

Change-Id: I46fcb9243722313b98e5c54e8194d6152f7e1631
2023-07-21 15:27:31 +00:00
Michal Niewrzal
d9525a0f27 cmd/tools/migrate-segment-copies: fix placement scan from DB
Placement can be null in DB and we need adjust scanning this column
from DB.

Additionally this change sets application name for DB connection.

Change-Id: I3c7d6294f4a3e5e441160b2fd4aeafffe705ec76
2023-07-21 13:12:08 +00:00
Vitalii
6e4044b245 satellite/payments: slightly refactor user upgrade observer of billing chore
Resolves post-merge comments from here
https://review.dev.storj.io/c/storj/storj/+/10780

Reworked some definitions. Added checks and comments.

Change-Id: I19c63804ad1f30a1ffd8cb87e96f43deed20a685
2023-07-21 03:43:42 +00:00
Wilfred Asomani
2c934d1cfd satellite/console: trigger invoice payment on card remove
This change triggers payment when a card is removed.

Issue: https://github.com/storj/storj/issues/6056

Change-Id: Ia1f2b49609ec9d111cbffb0757bec0b513abb79d
2023-07-20 15:46:50 +00:00
Egon Elbre
dc1509ee42 Makefile,web: bump to node@18
Change-Id: Ib50e18ecda9520a802a5a565e4dfe034ae41e98a
2023-07-20 16:44:13 +03:00
Artur M. Wolff
d45bc879c3 {Jenkinsfile, Makefile}: update Go to 1.20.6
Change-Id: I0a9b483ddcf2e0322840b1eb61982a21cea88e72
2023-07-20 09:20:30 +00:00
Egon Elbre
a058b7e982 web/satellite: fix npm for windows
`NODE_ENV=development xyz` approach does not work with Windows.

Change-Id: I3f0c4a6a2e9566df4923d5e6bc317c7d09573f18
2023-07-20 07:59:03 +00:00
Egon Elbre
7d1031feda satellite/accounting: fix duplicate code-block
Change-Id: Ic6de7337a5ff9a2fec73aa3d8a74ba5b9076a8c4
2023-07-20 07:58:53 +00:00
Jeremy Wharton
ca263c05bb web/satellite/vuetify-poc: go to all projects dashboard on logo click
This change navigates users to the all projects dashboard when the logo
in the navigation bar has been clicked.

Change-Id: Id63b616d12eea510b52f715f3003838541d2ab63
2023-07-19 12:06:29 -05:00
Clement Sam
4cb85186b2 storagenode/pieces: enable lazyfilewalker by default
Resolves https://github.com/storj/storj/issues/5861

Change-Id: I20e0a5b8a15ca966cbccd71369322a517a2c2130
2023-07-19 15:25:20 +00:00
paul cannon
9a871bf3bc go.mod: bump storj.io/common
In particular, to get commit 9db74ed9 in for the next storagenode build.

Change-Id: If07283bc72d66dfbd0ee91bc1cc8b2ce015871cd
2023-07-19 09:50:06 -05:00
Jeremy Wharton
afae5b578e web/satellite/vuetify-poc: allow navigation drawer to be toggled
This change allows the navigation drawer to be toggled by clicking the
the hamburger menu in the navigation bar. The hamburger menu has been
hidden in pages not containing a navigation drawer.

Resolves #6034

Change-Id: I48cfee1f48964c500c07f09f188c7077442261ab
2023-07-19 12:09:05 +00:00
Vitalii
5317135416 satellite/payments: fix config value for auto upgrade user tier flow
Fixed config value which indicates how many base units of US micro dollars are needed to auto upgrade user to paid tier.

Change-Id: I22821ac22fc3eaeeea21c6dec4e6912025df63aa
2023-07-19 10:43:07 +00:00
Wilfred Asomani
7cc873a62a satellite/payments: prevent removing other users' cards
This change patches a loophole that allowed accounts to remove cards
that belong to other users.

Closes #storj/storj-private#326

Change-Id: I33e9efe5c9cdb03aa48ad4c6b1d3283c396a7890
2023-07-19 09:44:06 +00:00
Michal Niewrzal
31bb6d54c7 cmd/tools: add tool to migrate segment copies metadata
We need migrate all existing segment copies to contain all the same
metadata as original segment. So far we were not duplicating stored
pieces but we are changing this behavior right now. We will use this
tool after enabling new way of doing server side copies.

Fixes https://github.com/storj/storj/issues/5890

Change-Id: Ia9ca12486f3c527abd28949eb438d1c4c7138d55
2023-07-18 15:12:51 +00:00
Egon Elbre
23631dc8bb satellite/accounting: fix TestProjectSegmentLimit*
Tally ensures that live accounting has the latest information,
however, when there are concurrent updates to live-accounting
it may by off by a few segments. Disable tally for those tests.

Change-Id: I6fa8a1794334bba093e18f29cb76e7b8d1244979
2023-07-18 14:26:05 +00:00
Egon Elbre
b1e7d70a86 satellite/payments/billing: fix test
`time.Now()` can return the same value when called sequentially.

Change-Id: I800c7696b919ad073e4558fb51c8d2eb4a04f05e
2023-07-18 16:25:42 +03:00
Vitalii
583ad54d86 satellite/{payments, console}: added functionality to get wallet's transactions (including pending)
Added new functionality to query storjscan for all wallet transactions (including pending).
Added new endpoint to query all wallet transactions.

Issue:
https://github.com/storj/storj/issues/5978

Change-Id: Id15fddfc9c95efcaa32aa21403cb177f9297e1ab
2023-07-18 11:09:29 +00:00
Vitalii
2ee0195eba satellite/payments: extend billing chore functionality to upgrade user
Added new observer for billing chore to check user's balance and upgrade their account if balance is more than or equal to needed amount for upgrade.
Added new config value which stands for needed amount of base units of US micro dollars needed to upgrade user.

Issue:
https://github.com/storj/storj/issues/5978

Change-Id: Ic3992cd3114397bfdd9e231ca090ff21ca66648b
2023-07-18 13:15:02 +03:00
Michal Niewrzal
0303920da7 satellite/metainfo: remove unused method
Change-Id: I08e307e6909cdc46951c5f3112d77a685e67fe2e
2023-07-18 08:45:29 +00:00
Jeremy Wharton
df9a6e968e web/satellite: lint Vuetify files
This change enables linting for the Vuetify proof of concept code and
fixes the linting errors that were detected. Additionally, it migrates
the Vuetify components to the composition API.

Change-Id: Id8cc083954e3f4cb66a00ad2715a96c8747b592c
2023-07-17 20:32:59 +00:00
dlamarmorgan
abe1463a73 payments/stripe/invoices: add token payment to overdue invoice payment
Add an attempt to pay overdue invoices via storj token if the user has
a token balance.

Change-Id: I819b89e7cf9cdb7deb9a51eab5ca684b54418218
2023-07-17 12:59:33 -07:00
dlamarmorgan
c96c83e805 satellite/payments/stripe/service: add manual payment with token command
Add the ability to pay an individual users open invoices using their
storj token balance.

Change-Id: I6115f2b033fd77f109ded6f55b1f35fc77c71ff1
2023-07-17 19:24:36 +00:00
dlamarmorgan
0f4371e84c scripts/tests/{backwardcompatibility,integrations}: add test scripts
Change-Id: Ib83cd0f083bab7f560a200fd95e62e5b21e60c27
2023-07-17 18:39:18 +00:00
Wilfred Asomani
0a8115b149 satellite/{console,payments}: fix handling for autofreeze flow
This change adds an extra step to the auto freeze chore to attempt
payment before freezing/warning a user.
It also attempts payment after modifying user's cards whether the user
is frozen/warned or not.

Issue: https://github.com/storj/storj-private/issues/341

Change-Id: Ia9c0c5a2d37837bca5153fe720fef61f1385cb15
2023-07-17 17:37:11 +00:00
Michal Niewrzal
47a4d4986d satellite/repair: enable declumping by default
This feature flag was disabled by default to test it slowly. Its enabled
for some time on one production satellite and test satellites without
any issue. We can enable it by default in code.

Change-Id: If9c36895bbbea12bd4aefa30cb4df912e1729e4c
2023-07-17 15:02:35 +00:00
Michal Niewrzal
5272fd8497 satellite/metainfo: do full bucket validation only on create
We are doing full bucket name validation for many requests but
we should do this only while creating bucket. Other requests will be
covered only by basic name length validation. Less strict validation for
other requests will make bucket usable in case of invalid bucket names
in DB (we have such cases from the past).

https://github.com/storj/storj/issues/6044

Change-Id: I3a41050e3637787f788705ef15b5dc4df4d01fc6
2023-07-17 16:15:33 +02:00
Jeremy Wharton
95761908b5 web/satellite: update Vuetify proof of concept
The changes as of storj/vuetify-storj@c801fe6 have been pulled into the
Vuetify proof of concept.

Change-Id: I3db208836cff21287052615d36258fcf2d4c6169
2023-07-14 12:46:50 +00:00
Michal Niewrzal
5234727886 satellite/repair/repairer: fix flaky TestSegmentRepairPlacement
Sometimes DownloadSelectionCache doesn't keep up with all node
placement changes we are doing during this test.

Change-Id: Idbda6511e3324b560cee3be85f980bf8d5b9b7ef
2023-07-14 10:10:40 +00:00
Tomasz Melcer
5a1c3f7f19
storage/reputation: logging changes to node scores (#5877)
Useful for monitoring storage nodes using log-parsing tools, like swatchdog.

Co-authored-by: Clement Sam <clementsam75@gmail.com>
2023-07-13 17:03:18 +02:00
Wilfred Asomani
4ee647a951 satellite: add request id to requests
This change adds request IDs to requests, logs them as part of audit
logs and sends to the client on error. This is to improve debugging
of customer issues.

Issue: https://github.com/storj/storj/issues/5898

Change-Id: I801514b547d28d810552d91aa7c8502051e552bf
2023-07-13 09:22:43 +00:00
Cameron
e8fcdc10a4 satellite/metainfo: set user_agent in bucket_metainfos on bucket recreation
Before this change, if a user creates a bucket with a user_agent attributed then deletes and recreates it, the row in bucket_metainfos
will not have the user_agent. This is because we skip setting the field
in bucket_metainfos if the bucket already exists in value_attributions.
This can be problematic, as we return the bucket's user agent during the
ListBuckets operation, and the client may be expecting this value to be
populated.

This change ensures the bucket table user_agent is set when (re)creating a bucket. To avoid decreasing BeginObject performance, which also
updates attribution, a flag has been added to determine whether to
make sure the buckets table is updated: `forceBucketUpdate`.

Change-Id: Iada2f233b327b292ad9f98c73ea76a1b0113c926
2023-07-12 21:48:05 +00:00
Michal Niewrzal
99128ab551 satellite/metabase: reuse Pieces while looping segments
Segments loop implementation is using lots of memory to convert
alias pieces to pieces for each segment while iteration. To improve
situation this change is reusing Pieces between batch pages. This
should signifcantly reduce memory usage for ranged loop executions.

Change-Id: I469188779908facb19ad85c6bb7bc3657111cc9a
2023-07-12 09:29:34 +00:00
Jeremy Wharton
062ca285a0 web/satellite: add sharing option to dropdown in buckets page
This change adds a sharing option to the dropdown menu for bucket rows
in the Buckets page.

Resolves #5964

Change-Id: Ife0eb8f6cabbe85eaedae1d94d97694f3c677a3e
2023-07-11 22:03:42 +00:00
Egon Elbre
465941b345 satellite/{nodeselection,overlay}: use location.Set
location.Set is faster for comparisons.

Updates #6028

Change-Id: I764eb5cafc507f908e4168b16a7994cc7721ce4d
2023-07-11 17:16:30 +00:00
Cameron
7e03ccfa46 satellite/console: optional separate web app server
This change creates the ability to run a server separate from the
console web server to serve the front end app. You can run it with
`satellite run ui`. Since there are now potentially two servers instead
of one, the UI server has the option to act as a reverse proxy to the
api server for local development by setting `--console.address` to the
console backend address and `--console.backend-reverse-proxy` to the
console backend's http url. Also, a feature flag has been implemented
on the api server to retain the ability to serve the front end app. It
is toggled with `--console.frontend-enable`.

github issue: https://github.com/storj/storj/issues/5843

Change-Id: I0d30451a20636e3184110dbe28c8a2a8a9505804
2023-07-11 12:17:35 -04:00
Egon Elbre
9370bc4580 satellite/{nodeselection,overlay}: bump common and fix some potential issues
* Handle failed country code conversion.
* Avoid potential issues with a data-race due to shared slice.

Updates #6028

Change-Id: If7beef2619abd084e1f4109de2d323f834a6090a
2023-07-11 11:13:41 +00:00
Michal Niewrzal
1f92e7acda satellite: move GC sender to Core peer
Having separate process/pod only for sending bloom filters once a week
is a bit waste. After reconfiguring production settings to use sender in
core we can remove also GC sender peer code.

https://github.com/storj/storj/issues/5979

Change-Id: I6efe3ec073f96545e1f70ad13843f8ccdf923ee8
2023-07-11 10:31:35 +00:00
Vitalii
a9d979e4d7 web/satellite: update default multipart upload part size
Updated multipart upload part size to be 64MB or higher depending on file size.
Increased queue size from 4 to 5 (5 parts being uploaded at a time) because theoretically it can decrease uploading time, right?

Issue:
https://github.com/storj/storj/issues/5851

Change-Id: Ida5661fa0ed6bc5a0651afc05b5feb7e77791efe
2023-07-10 20:07:51 +03:00
Moby von Briesen
4108aa72ba satellite/console,web/satellite: Fix project limit checking
* Fixes backend to use only a user's owned projects to determine if the
  user has hit the project limit
* Makes frontend logic consistent (and simpler) for checking whether to
  send user to the "Create Project" modal or the "upgrade account or
  request limit increase" modal

Before this change, projects that a user is a member of would be
included in determining whether the user could create a project. Also,
the "create project" button in the projects menu in the navbar of the UI
did not enable a free tier user to create a new project, even if they
had not hit their limits.

Change-Id: Ia776eb627ca37b83f5bc63bed83ee83c9f7cc789
2023-07-10 15:51:00 +00:00
Jeremy Wharton
4e876fbdba web/satellite: update upload modal
The upload modal has been updated to more closely match our designs.
- A button has been added to cancel all in-progress uploads.
- The status text has been fixed to display the proper file count.
- Clicking completed items in the upload modal previews them.

Resolves #5973

Change-Id: Iaee5fe05be14b3a6f2de1a9c807eca5137c7d643
2023-07-10 15:12:31 +00:00
Moby von Briesen
bd4d57c604 satellite/payments: Exclude users who pay via storjscan from autofreeze
Add a configuration (default true) to exclude users who have made
storjscan payments from being auto-warned/frozen for an unpaid invoice.
This will allow us to reach out to these users and handle warning/freezing
manually. Auto account freeze still handles CC-only users.

Fixes https://github.com/storj/storj/issues/6027

Change-Id: I0c862785dad1c8febfa11100c0d30e621ce3ae9b
2023-07-10 13:39:01 +00:00
Jeremy Wharton
c79d1b0d2f {satellite/console,web/satellite}: show error for project invite dupes
This change fixes an issue where a new project member invitation would
silently replace an older one that has the same project ID and email if
the email did not belong to a registered user. Additionally, the
satellite frontend has been updated to display more descriptive error
messages for project member invitations.

Change-Id: I32b582c40c0028b8eedf2aed4b5bfb43501594b4
2023-07-10 12:56:02 +00:00
Jeremy Wharton
fbda13c752 {satellite/console,web/satellite}: trim emails when inviting members
This change trims whitespace from email addresses in project member
invitation requests.

Change-Id: Idd9116820897bf29f3eeba8cf95770b1aa14690c
2023-07-10 12:22:07 +00:00
Jeremy Wharton
0f9a0ba9cd web/satellite: fix project switch when removing project members
This change fixes an issue where the currently selected project would
be switched when removing project members.

Change-Id: I8138b4229eb7933d25a2fe84e5aa0b5846fc79b8
2023-07-10 11:46:49 +00:00
JT Olio
73d65fce9a cmd/satellite/billing: don't fail the overall process if an individual invoice fails
Change-Id: I36591a717ef97bdb417cc6d9218e22b2f91f249b
2023-07-10 11:13:23 +00:00
Michal Niewrzal
1d62dc63f5 satellite/repair/repairer: fix NumHealthyInExcludedCountries calculation
Currently, we have issue were while counting unhealthy pieces we are
counting twice piece which is in excluded country and is outside segment
placement. This can cause unnecessary repair.

This change is also doing another step to move RepairExcludedCountryCodes
from overlay config into repair package.

Change-Id: I3692f6e0ddb9982af925db42be23d644aec1963f
2023-07-10 12:01:19 +02:00
Igor
05f30740f5
docs/testplan: add project cowbell testplan (#6001) 2023-07-10 11:23:55 +02:00
Márton Elek
97a89c3476 satellite: switch to use nodefilters instead of old placement.AllowedCountry
placement.AllowedCountry is the old way to specify placement, with the new approach we can use a more generic (dynamic method), which can check full node information instead of just the country code.

The 90% of this patch is just search and replace:

 * we need to use NodeFilters instead of placement.AllowedCountry
 * which means, we need an initialized PlacementRules available everywhere
 * which means we need to configure the placement rules

The remaining 10% is the placement.go, where we introduced a new type of configuration (lightweight expression language) to define any kind of placement without code change.

Change-Id: Ie644b0b1840871b0e6bbcf80c6b50a947503d7df
2023-07-07 16:55:45 +00:00
Wilfred Asomani
e0b5476e78 web/satellite: fix long table content overflow
This change fixes an issue where long text content in the common table
will overflow and breaks layout.

Change-Id: I30f6e08488410359e0a97995f8d769b272b56c72
2023-07-07 12:24:37 +00:00
Jeremy Wharton
074457fa4e web/satellite: add folder sharing
This change allows users to generate links for sharing folders.
Previously, users were only able to do this with files and buckets.

Resolves #5644

Change-Id: I16dd8270337e3561b6bda895b46d3cc9be5f8041
2023-07-07 10:03:06 +00:00
Vitalii
5fc6eaab17 satellite/{console, web}: display accurate legacy free tier information in upgrade modal
Updated upgrade account modal to show user account free tier limits instead of hardcoded values.

Issue:
https://github.com/storj/storj/issues/5939

Change-Id: I26ffbe2571c5ca4b37f02bec5211bac986bedc6a
2023-07-07 09:23:36 +00:00
Márton Elek
70cdca5d3c
satellite: move satellite/nodeselection/uploadselection => satellite/nodeselection
All the files in uploadselection are (in fact) related to generic node selection, and used not only for upload,
but for download, repair, etc...

Change-Id: Ie4098318a6f8f0bbf672d432761e87047d3762ab
2023-07-07 10:32:03 +02:00
Márton Elek
8b4387a498 satellite/satellitedb: add tag information to nodes selected for upload/downloads
Change-Id: I0fa7daebcf83f7949726e5fffe68e0bdc6fd1d7a
2023-07-07 07:54:16 +00:00
Vitalii
ced8657caa web/satellite: removed unused images
Change-Id: Ifd9c691c69f50a8f346ac4ac2fa1433b00ea81b9
2023-07-06 20:31:24 +00:00
Vitalii
ece0cc5785 web/satellite: fix bottom spacing for all pages
Fixed bottom spacing for all pages. Basically removed bottom padding override in dashboard wrapper.
Removed a couple of unused components and icons.
Made pagination size changer dropdown to appear on top of selector to not extend page height.

This change fixes pagination issue on Team page:
https://github.com/storj/storj/issues/6012

Change-Id: I707dd1bf9d61b05742b7f97a757a2a8f5f9f93fd
2023-07-06 19:57:43 +00:00
JT Olio
a85c080509 docs/blueprints: certified nodes
Change-Id: I7c670d740e4c3d1035dee145ed65aaed4cbaba0c
2023-07-06 14:07:46 -04:00
paul cannon
a4d68b9b7e satellite/metabase: server-side copy copies metadata
..instead of using segment_copies and ancestor_stream_id, etc.

This bypasses reference counting entirely, depending on our mark+sweep+
bloomfilter garbage collection strategy to get rid of pieces once they
are no longer part of a segment.

This is only safe to do after we have stopped passing delete requests on
to storage nodes.

Refs: https://github.com/storj/storj/issues/5889
Change-Id: I37bdcffaa752f84fd85045235d6875b3526b5ecc
2023-07-06 14:40:59 +00:00
Márton Elek
ddf1f1c340 satellite/{nodeselection,overlay}: NodeFilters for dynamic placement implementations
Change-Id: Ica3a7b535fa6736cd8fb12066e615b70e1fa65d6
2023-07-06 12:08:01 +00:00
Vitalii
e3d2f09988 web/satellite: add support link to upgrade account STORJ token flow
Added support link to upgrade account STORJ token flow to tell user that they have to fill in another form to upgrade with tokens only.

Issue:
https://github.com/storj/storj/issues/5985

Change-Id: Ifb852b883c6bf092d5eec588e823925a8ea661c9
2023-07-06 08:38:18 +00:00
Ethan Adams
f819b6a210 satellite/entrypoint: Ignore unset variable errors while checking for VALID_EXECUTABLE
Otherwise core and ranged loop fail at startup with "/entrypoint: line 94: $1: unbound variable"

Change-Id: I45a318038cd937c11f6a00d506c339ba69ea07bf
2023-07-05 20:18:38 +00:00
Márton Elek
1525324384 satellite/uploadselection: avoid String conversation of location during node selection
Converting location to String is not free, better to avoid it.

81cb588c23/storj/location/countrycode.go (L32)

Thanks to Egon, who reported this issue.

See also: https://review.dev.storj.io/c/storj/common/+/10732

Change-Id: Ife348cffa59c020b46914a68be231c6eb75f06c9
2023-07-05 19:22:12 +00:00
Michal Niewrzal
2c3464081f satellite/metainfo: fix bucket name validation
Change-Id: Ifa400ec855ee978ff001fa3736a8a4c1c53fd18c
2023-07-05 14:42:31 +00:00
Márton Elek
6a3802de4f satellite,storagenode: propagate node tags with NodeCheckin
Change-Id: Ib1a602a8cf81204efa001b5d338914ea4218c39b
2023-07-05 13:45:42 +00:00
Clement Sam
a740f96f75 storagenode/pieces/lazyfilewalker: test zapwrapper
This add tests to the zapwrapper package and also adds a test
to verify the issue in https://github.com/storj/storj/issues/6006

Change-Id: Iec3f568e72683af71e1718017109a1ed52794b0b
2023-07-05 12:33:00 +00:00
Clement Sam
7ac2031cac web/multinode: fix wrong free disk space in allocation on dashboard
There are many case where the keywords `free` and `available`
are confused in their usage.

For most cases, `free` space is the amount of free space left
on the whole disk, and not just in allocation while
`available` space is the amount of free space left in the
allocated disk space.

What the user/sno wants to see is not the free space but the
available space. To the SNO, free space is the free space
left in the allocated disk space.

Because of this confusion, the multinode dashboard displays
the `free` disk space instead of the free space in the
allocated disk space https://github.com/storj/storj/issues/5248
While the storagenode dashboard shows the correct free space
in the allocation.

This change fixes the wrong free disk space. I also added a
few comments to make a distinction between the `free`
and `available` fields in the `DiskSpace*` structs.

Change-Id: I11b372ca53a5ac05dc3f79834c18f85ebec11855
2023-07-05 11:24:24 +00:00
Michal Niewrzal
21c1e66a85 satellite/overlay: refactor ReliabilityCache to keep more data
ReliabilityCache will be now using refactored overlay Reliable method.
This method will provide more info about nodes (e.g. country code) and
with this we are able to add two dedicated methods to classify pieces:
* OutOfPlacementPieces
* PiecesNodesLastNetsInOrder

With those new method we will fix issue where offline but reliable node
won't be checked for clumped pieces and off placement pieces.

https://github.com/storj/storj/issues/5998

Change-Id: I9ffbed9f07f4881c9db3bd0e5f0412f1a418dd82
2023-07-05 11:19:10 +02:00
Michal Niewrzal
f2cd7b0928 satellite/overlay: refactor Reliable to be used with repair checker
Currently we are using Reliable to get missing pieces for repair
checker. The issue is that now checker is looking at more things than
just missing pieces (clumped/off, placement pieces) and using only node
ID is not enough. We have issue where we are skipping offline nodes from
clumped and off placement pieces check.

Reliable was refactored to get data (e.g. country, lastNet) about all
reliable nodes. List is split into online and offline. This data will be
cached for quick use by repair checker. It will be also possible to
check nodes metadata like country code or lastNet.

We are also slowly moving `RepairExcludedCountryCodes` config from
overlay to repair which makes more sens for it.

This this first part of changes.

https://github.com/storj/storj/issues/5998

Change-Id: If534342488c0e440affc2894a8fbda6507b8959d
2023-07-05 10:56:31 +02:00
Márton Elek
500b6244f8
satellite/satellitedb: create table for node tags
Change-Id: I884bb740974e6b8241aa6b85faf266b85fe892d4
2023-07-05 09:38:53 +02:00
Clement Sam
1851d103f9 go.mod: bump storj.io/private
Updates https://github.com/storj/storj/issues/6006

Change-Id: I1f549d5642213e420f3e5d0df4ef972c77add713
2023-07-03 23:59:15 +00:00
paul cannon
032546219c satellite/admin: fix spelling of list-apikeys endpoint
Currently, any attempt to list the api keys associated with a project
from the admin UI results in a 404 NOT FOUND error.

This appears to be because there is no /api/projects/{project}/apiKeys
endpoint registered; it should have a lowercase k.

Change-Id: Ifbe4cd0f9ba12a6e37a0d9f64df91c264ced5558
2023-07-03 21:03:53 +00:00
Jeremy Wharton
1173877167 {satellite/console,web/satellite}: encode email in project invite URLs
This change properly encodes email addresses that are used as query
parameters in project invitation-related URLs.

Change-Id: Iaaf7b62b5ac3db3f0b0e000cc06fef8e315400a8
2023-07-03 18:07:19 +00:00
Vitalii
cb41c51692 web/satellite: abort request to get object count in 10 seconds
When entering passphrase to open bucket we make a ListObjectsV2Command request to figure out if there are objects encrypted with different passphrase.
If there are a lot of objects then this request takes forever to process.
By this change I added 10 seconds timeout for that request to not block user.

Issue:
https://github.com/storj/storj/issues/5954

Change-Id: I5243fba68d0b56dff2fb2b3a608a5e71860724c2
2023-07-03 17:33:11 +00:00
Márton Elek
d38b8fa2c4 satellite/nodeselection: use the same Node object from overlay and nodeselection
We use two different Node types in `overlay` and `uploadnodeselection` and converting back and forth.

Using the same object would allow us to use a unified node selection interface everywhere.

Change-Id: Ie71e29d60184ee0e5b4547eb54325f09c418f73c
2023-07-03 16:59:33 +00:00
Márton Elek
20a47034a5
cmd/tools: tag-signer utility to create signed node tags
Change-Id: I2983d688a109325a02fcd060ca1a2d4eb8e9e931
2023-07-03 18:10:08 +02:00
Márton Elek
01e33e7753 cmd/satellite: make satellite docker image compatible with storj-up
This patch makes satellite container images compatible with storj-up.

Which means that any official release can be easily tested locally.

It means that we need some binaries (like storj-up, dlv) and shall fragments part of the production image, but I think the risk is very low and the benefit is very high.

This is the first towards to unify all the images and make it possible to test/run the same components everywhere (storj-up/nigttly/prod).

https://github.com/storj/storj/issues/5946

Change-Id: Ib53b6213d94f93a793a841dedfe32cc59ef69b72
2023-07-03 15:02:27 +00:00
Márton Elek
8482b37c14
go.mod: bump to use latest common
Change-Id: Ie31e7779e86d13ca3c8acaacfe6ed1e23f2c9740
2023-07-03 13:32:35 +02:00
Wilfred Asomani
f131047f1a web/satellite: match projects table with the designs
This change updates the projects table on all projects dashboard to
more closely match the designs.

Change-Id: I547a83352fba8c3ad7958802db7b38b342b383e8
2023-06-30 12:05:47 +00:00
Vitalii
8d8f6734de satellite/{db, accounting}: added functionality to query settled bandwidth for given project
Added functionality to return only settled traffic from project_bandwidth_daily_rollups table for given month.
Updated {projectID}/usage-limits endpoint to return only settled bandwidth used.

This is a possible fix for this issue
https://github.com/storj/storj-private/issues/293

Change-Id: I12516dc898f449c2122e7442b8fbb88309a48ebe
2023-06-30 13:24:16 +03:00
Jeremy Wharton
c006126d54 web/satellite: add Back button to Bucket Details page
A button has been added to the Bucket Details page that returns the
user to the file browser.

Change-Id: Ic2868b1fc9e3b2b0e9785728dc7a116c828eced8
2023-06-30 08:53:36 +00:00
975 changed files with 58163 additions and 28654 deletions

View File

@ -35,6 +35,7 @@ satellite-web:
RUN ./build.sh
COPY +wasm/wasm static/wasm
SAVE ARTIFACT dist AS LOCAL web/satellite/dist
SAVE ARTIFACT dist_vuetify_poc AS LOCAL web/satellite/dist_vuetify_poc
SAVE ARTIFACT static AS LOCAL web/satellite/static
satellite-admin:
@ -119,6 +120,7 @@ build-tagged-image:
FROM img.dev.storj.io/storjup/base:20230208-1
COPY +multinode-web/dist /var/lib/storj/storj/web/multinode/dist
COPY +satellite-web/dist /var/lib/storj/storj/web/satellite/dist
COPY +satellite-web/dist_vuetify_poc /var/lib/storj/storj/web/satellite/dist_vuetify_poc
COPY +satellite-admin/build /app/satellite-admin/
COPY +satellite-web/static /var/lib/storj/storj/web/satellite/static
COPY +storagenode-web/dist /var/lib/storj/storj/web/storagenode/dist

4
Jenkinsfile vendored
View File

@ -32,7 +32,7 @@ node('node') {
sh 'docker exec postgres-$BUILD_NUMBER createdb -U postgres teststorj'
// fetch the remote main branch
sh 'git fetch --no-tags --progress -- https://github.com/storj/storj.git +refs/heads/main:refs/remotes/origin/main'
sh 'docker run -u $(id -u):$(id -g) --rm -i -v $PWD:$PWD -w $PWD --entrypoint $PWD/scripts/tests/testversions/test-sim-versions.sh -e STORJ_SIM_POSTGRES -e STORJ_SIM_REDIS --link redis-$BUILD_NUMBER:redis --link postgres-$BUILD_NUMBER:postgres storjlabs/golang:1.20.3'
sh 'docker run -u $(id -u):$(id -g) --rm -i -v $PWD:$PWD -w $PWD --entrypoint $PWD/scripts/tests/testversions/test-sim-versions.sh -e STORJ_SIM_POSTGRES -e STORJ_SIM_REDIS --link redis-$BUILD_NUMBER:redis --link postgres-$BUILD_NUMBER:postgres storjlabs/golang:1.20.6'
}
catch(err){
throw err
@ -69,7 +69,7 @@ node('node') {
sh 'docker exec postgres-$BUILD_NUMBER createdb -U postgres teststorj'
// fetch the remote main branch
sh 'git fetch --no-tags --progress -- https://github.com/storj/storj.git +refs/heads/main:refs/remotes/origin/main'
sh 'docker run -u $(id -u):$(id -g) --rm -i -v $PWD:$PWD -w $PWD --entrypoint $PWD/scripts/tests/rollingupgrade/test-sim-rolling-upgrade.sh -e BRANCH_NAME -e STORJ_SIM_POSTGRES -e STORJ_SIM_REDIS -e STORJ_MIGRATION_DB --link redis-$BUILD_NUMBER:redis --link postgres-$BUILD_NUMBER:postgres storjlabs/golang:1.20.3'
sh 'docker run -u $(id -u):$(id -g) --rm -i -v $PWD:$PWD -w $PWD --entrypoint $PWD/scripts/tests/rollingupgrade/test-sim-rolling-upgrade.sh -e BRANCH_NAME -e STORJ_SIM_POSTGRES -e STORJ_SIM_REDIS -e STORJ_MIGRATION_DB --link redis-$BUILD_NUMBER:redis --link postgres-$BUILD_NUMBER:postgres storjlabs/golang:1.20.6'
}
catch(err){
throw err

View File

@ -1,8 +1,8 @@
GO_VERSION ?= 1.20.3
GO_VERSION ?= 1.20.6
GOOS ?= linux
GOARCH ?= amd64
GOPATH ?= $(shell go env GOPATH)
NODE_VERSION ?= 16.11.1
NODE_VERSION ?= 18.17.0
COMPOSE_PROJECT_NAME := ${TAG}-$(shell git rev-parse --abbrev-ref HEAD)
BRANCH_NAME ?= $(shell git rev-parse --abbrev-ref HEAD | sed "s!/!-!g")
GIT_TAG := $(shell git rev-parse --short HEAD)
@ -73,6 +73,8 @@ build-multinode-npm:
cd web/multinode && npm ci
build-satellite-admin-npm:
cd satellite/admin/ui && npm ci
# Temporary until the new back-office replaces the current admin API & UI
cd satellite/admin/back-office/ui && npm ci
##@ Simulator
@ -126,7 +128,7 @@ lint:
-v ${GOPATH}/pkg:/go/pkg \
-v ${PWD}:/storj \
-w /storj \
storjlabs/ci-slim \
storjlabs/ci:slim \
make .lint LINT_TARGET="$(LINT_TARGET)"
.PHONY: .lint/testsuite/ui
@ -286,6 +288,14 @@ satellite-admin-ui:
-u $(shell id -u):$(shell id -g) \
node:${NODE_VERSION} \
/bin/bash -c "npm ci && npm run build"
# Temporary until the new back-office replaces the current admin API & UI
docker run --rm -i \
--mount type=bind,src="${PWD}",dst=/go/src/storj.io/storj \
-w /go/src/storj.io/storj/satellite/admin/back-office/ui \
-e HOME=/tmp \
-u $(shell id -u):$(shell id -g) \
node:${NODE_VERSION} \
/bin/bash -c "npm ci && npm run build"
.PHONY: satellite-wasm
satellite-wasm:
@ -464,7 +474,9 @@ binaries-upload: ## Upload binaries to Google Storage (jenkins)
zip -r "$${zipname}.zip" "$${filename}" \
; fi \
; done
cd "release/${TAG}"; gsutil -m cp -r *.zip "gs://storj-v3-alpha-builds/${TAG}/"
cd "release/${TAG}" \
&& sha256sum *.zip > sha256sums \
&& gsutil -m cp -r *.zip sha256sums "gs://storj-v3-alpha-builds/${TAG}/"
.PHONY: draft-release
draft-release:

View File

@ -1,34 +1,54 @@
ARG DOCKER_ARCH
# Satellite UI static asset generation
FROM node:16.11.1 as ui
FROM node:18.17.0 as ui
WORKDIR /app
COPY web/satellite/ /app
# Need to clean up (or ignore) local folders like node_modules, etc...
RUN npm install
RUN npm run build
RUN npm run build-vuetify
# Fetch ca-certificates file for arch independent builds below
FROM debian:buster-slim as ca-cert
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates
RUN update-ca-certificates
# Install storj-up helper (for local/dev runs)
FROM --platform=$TARGETPLATFORM golang:1.19 AS storjup
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go install storj.io/storj-up@latest
# Install dlv (for local/dev runs)
FROM --platform=$TARGETPLATFORM golang:1.19 AS dlv
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go install github.com/go-delve/delve/cmd/dlv@latest
FROM ${DOCKER_ARCH:-amd64}/debian:buster-slim
ARG TAG
ARG GOARCH
ENV GOARCH ${GOARCH}
ENV CONF_PATH=/root/.local/share/storj/satellite \
STORJ_CONSOLE_STATIC_DIR=/app \
STORJ_MAIL_TEMPLATE_PATH=/app/static/emails \
STORJ_CONSOLE_ADDRESS=0.0.0.0:10100
ENV PATH=$PATH:/app
EXPOSE 7777
EXPOSE 10100
WORKDIR /app
COPY --from=ui /app/static /app/static
COPY --from=ui /app/dist /app/dist
COPY --from=ui /app/dist_vuetify_poc /app/dist_vuetify_poc
COPY --from=ca-cert /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY release/${TAG}/wasm/access.wasm /app/static/wasm/
COPY release/${TAG}/wasm/wasm_exec.js /app/static/wasm/
COPY release/${TAG}/wasm/access.wasm.br /app/static/wasm/
COPY release/${TAG}/wasm/wasm_exec.js.br /app/static/wasm/
COPY release/${TAG}/satellite_linux_${GOARCH:-amd64} /app/satellite
COPY --from=storjup /go/bin/storj-up /usr/local/bin/storj-up
COPY --from=dlv /go/bin/dlv /usr/local/bin/dlv
# test identities for quick-start
COPY --from=img.dev.storj.io/storjup/base:20230607-1 /var/lib/storj/identities /var/lib/storj/identities
COPY cmd/satellite/entrypoint /entrypoint
ENTRYPOINT ["/entrypoint"]

View File

@ -1,6 +1,7 @@
#!/bin/bash
set -euo pipefail
## production helpers
SETUP_PARAMS=""
if [ -n "${IDENTITY_ADDR:-}" ]; then
@ -21,6 +22,10 @@ if [ "${SATELLITE_API:-}" = "true" ]; then
exec ./satellite run api $RUN_PARAMS "$@"
fi
if [ "${SATELLITE_UI:-}" = "true" ]; then
exec ./satellite run ui $RUN_PARAMS "$@"
fi
if [ "${SATELLITE_GC:-}" = "true" ]; then
exec ./satellite run garbage-collection $RUN_PARAMS "$@"
fi
@ -37,4 +42,63 @@ if [ "${SATELLITE_AUDITOR:-}" = "true" ]; then
exec ./satellite run auditor $RUN_PARAMS "$@"
fi
## storj-up helpers
if [ "${STORJUP_ROLE:-""}" ]; then
if [ "${STORJ_IDENTITY_DIR:-""}" ]; then
#Generate identity if missing
if [ ! -f "$STORJ_IDENTITY_DIR/identity.key" ]; then
if [ "$STORJ_USE_PREDEFINED_IDENTITY" ]; then
# use predictable, pre-generated identity
mkdir -p $(dirname $STORJ_IDENTITY_DIR)
cp -r /var/lib/storj/identities/$STORJ_USE_PREDEFINED_IDENTITY $STORJ_IDENTITY_DIR
else
identity --identity-dir $STORJ_IDENTITY_DIR --difficulty 8 create .
fi
fi
fi
if [ "${STORJ_WAIT_FOR_DB:-""}" ]; then
storj-up util wait-for-port cockroach:26257
storj-up util wait-for-port redis:6379
fi
if [ "${STORJUP_ROLE:-""}" == "satellite-api" ]; then
mkdir -p /var/lib/storj/.local
#only migrate first time
if [ ! -f "/var/lib/storj/.local/migrated" ]; then
satellite run migration --identity-dir $STORJ_IDENTITY_DIR
touch /var/lib/storj/.local/migrated
fi
fi
# default config generated without arguments is misleading
rm /root/.local/share/storj/satellite/config.yaml
mkdir -p /var/lib/storj/.local/share/storj/satellite || true
if [ "${GO_DLV:-""}" ]; then
echo "Starting with go dlv"
#absolute file path is required
CMD=$(which $1)
shift
/usr/local/bin/dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec --check-go-version=false -- $CMD "$@"
exit $?
fi
fi
# for backward compatibility reason, we use argument as command, only if it's an executable (and use it as satellite flags oterwise)
set +eo nounset
which "$1" > /dev/null
VALID_EXECUTABLE=$?
set -eo nounset
if [ $VALID_EXECUTABLE -eq 0 ]; then
# this is a full command (what storj-up uses)
exec "$@"
else
# legacy, run-only parameters
exec ./satellite run $RUN_PARAMS "$@"
fi

View File

@ -27,7 +27,7 @@ import (
)
// generateGracefulExitCSV creates a report with graceful exit data for exiting or exited nodes in a given period.
func generateGracefulExitCSV(ctx context.Context, completed bool, start time.Time, end time.Time, output io.Writer) error {
func generateGracefulExitCSV(ctx context.Context, timeBased bool, completed bool, start time.Time, end time.Time, output io.Writer) error {
db, err := satellitedb.Open(ctx, zap.L().Named("db"), reportsGracefulExitCfg.Database, satellitedb.Options{ApplicationName: "satellite-gracefulexit"})
if err != nil {
return errs.New("error connecting to master database on satellite: %+v", err)
@ -67,12 +67,15 @@ func generateGracefulExitCSV(ctx context.Context, completed bool, start time.Tim
if err != nil {
return err
}
exitProgress, err := db.GracefulExit().GetProgress(ctx, id)
exitProgress := &gracefulexit.Progress{}
if !timeBased {
exitProgress, err = db.GracefulExit().GetProgress(ctx, id)
if gracefulexit.ErrNodeNotFound.Has(err) {
exitProgress = &gracefulexit.Progress{}
} else if err != nil {
return err
}
}
exitStatus := node.ExitStatus
exitFinished := ""

View File

@ -40,7 +40,7 @@ import (
"storj.io/storj/satellite/accounting/live"
"storj.io/storj/satellite/compensation"
"storj.io/storj/satellite/metabase"
"storj.io/storj/satellite/overlay"
"storj.io/storj/satellite/nodeselection"
"storj.io/storj/satellite/payments/stripe"
"storj.io/storj/satellite/satellitedb"
)
@ -100,6 +100,11 @@ var (
Short: "Run the satellite API",
RunE: cmdAPIRun,
}
runUICmd = &cobra.Command{
Use: "ui",
Short: "Run the satellite UI",
RunE: cmdUIRun,
}
runRepairerCmd = &cobra.Command{
Use: "repair",
Short: "Run the repair service",
@ -255,12 +260,19 @@ var (
Long: "Finalizes all draft stripe invoices known to satellite's stripe account.",
RunE: cmdFinalizeCustomerInvoices,
}
payCustomerInvoicesCmd = &cobra.Command{
payInvoicesWithTokenCmd = &cobra.Command{
Use: "pay-customer-invoices",
Short: "pay open finalized invoices for customer",
Long: "attempts payment on any open finalized invoices for a specific user.",
Args: cobra.ExactArgs(1),
RunE: cmdPayCustomerInvoices,
}
payAllInvoicesCmd = &cobra.Command{
Use: "pay-invoices",
Short: "pay finalized invoices",
Long: "attempts payment on all open finalized invoices according to subscriptions settings.",
Args: cobra.ExactArgs(1),
RunE: cmdPayCustomerInvoices,
RunE: cmdPayAllInvoices,
}
stripeCustomerCmd = &cobra.Command{
Use: "ensure-stripe-customer",
@ -342,6 +354,7 @@ var (
Database string `help:"satellite database connection string" releaseDefault:"postgres://" devDefault:"postgres://"`
Output string `help:"destination of report output" default:""`
Completed bool `help:"whether to output (initiated and completed) or (initiated and not completed)" default:"false"`
TimeBased bool `help:"whether the satellite is using time-based graceful exit (and thus, whether to include piece transfer progress in output)" default:"false"`
}
reportsVerifyGracefulExitReceiptCfg struct {
}
@ -366,6 +379,7 @@ func init() {
rootCmd.AddCommand(runCmd)
runCmd.AddCommand(runMigrationCmd)
runCmd.AddCommand(runAPICmd)
runCmd.AddCommand(runUICmd)
runCmd.AddCommand(runAdminCmd)
runCmd.AddCommand(runRepairerCmd)
runCmd.AddCommand(runAuditorCmd)
@ -398,12 +412,14 @@ func init() {
billingCmd.AddCommand(createCustomerInvoicesCmd)
billingCmd.AddCommand(generateCustomerInvoicesCmd)
billingCmd.AddCommand(finalizeCustomerInvoicesCmd)
billingCmd.AddCommand(payCustomerInvoicesCmd)
billingCmd.AddCommand(payInvoicesWithTokenCmd)
billingCmd.AddCommand(payAllInvoicesCmd)
billingCmd.AddCommand(stripeCustomerCmd)
consistencyCmd.AddCommand(consistencyGECleanupCmd)
process.Bind(runCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runMigrationCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runAPICmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runUICmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runAdminCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runRepairerCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(runAuditorCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
@ -432,7 +448,8 @@ func init() {
process.Bind(createCustomerInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(generateCustomerInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(finalizeCustomerInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(payCustomerInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(payInvoicesWithTokenCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(payAllInvoicesCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(stripeCustomerCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(consistencyGECleanupCmd, &consistencyGECleanupCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
process.Bind(fixLastNetsCmd, &runCfg, defaults, cfgstruct.ConfDir(confDir), cfgstruct.IdentityDir(identityDir))
@ -644,7 +661,7 @@ func cmdReportsGracefulExit(cmd *cobra.Command, args []string) (err error) {
// send output to stdout
if reportsGracefulExitCfg.Output == "" {
return generateGracefulExitCSV(ctx, reportsGracefulExitCfg.Completed, start, end, os.Stdout)
return generateGracefulExitCSV(ctx, reportsGracefulExitCfg.TimeBased, reportsGracefulExitCfg.Completed, start, end, os.Stdout)
}
// send output to file
@ -657,7 +674,7 @@ func cmdReportsGracefulExit(cmd *cobra.Command, args []string) (err error) {
err = errs.Combine(err, file.Close())
}()
return generateGracefulExitCSV(ctx, reportsGracefulExitCfg.Completed, start, end, file)
return generateGracefulExitCSV(ctx, reportsGracefulExitCfg.TimeBased, reportsGracefulExitCfg.Completed, start, end, file)
}
func cmdNodeUsage(cmd *cobra.Command, args []string) (err error) {
@ -862,6 +879,18 @@ func cmdFinalizeCustomerInvoices(cmd *cobra.Command, args []string) (err error)
func cmdPayCustomerInvoices(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)
return runBillingCmd(ctx, func(ctx context.Context, payments *stripe.Service, _ satellite.DB) error {
err := payments.InvoiceApplyCustomerTokenBalance(ctx, args[0])
if err != nil {
return errs.New("error applying native token payments to invoice for customer: %v", err)
}
return payments.PayCustomerInvoices(ctx, args[0])
})
}
func cmdPayAllInvoices(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)
periodStart, err := parseYearMonth(args[0])
if err != nil {
return err
@ -885,6 +914,9 @@ func cmdStripeCustomer(cmd *cobra.Command, args []string) (err error) {
func cmdConsistencyGECleanup(cmd *cobra.Command, args []string) error {
ctx, _ := process.Ctx(cmd)
if runCfg.GracefulExit.TimeBased {
return errs.New("this command is not supported with time-based graceful exit")
}
before, err := time.Parse("2006-01-02", consistencyGECleanupCfg.Before)
if err != nil {
return errs.New("before flag value isn't of the expected format. %+v", err)
@ -932,7 +964,7 @@ func cmdRestoreTrash(cmd *cobra.Command, args []string) error {
successes := new(int64)
failures := new(int64)
undelete := func(node *overlay.SelectedNode) {
undelete := func(node *nodeselection.SelectedNode) {
log.Info("starting restore trash", zap.String("Node ID", node.ID.String()))
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
@ -966,9 +998,9 @@ func cmdRestoreTrash(cmd *cobra.Command, args []string) error {
log.Info("successful restore trash", zap.String("Node ID", node.ID.String()))
}
var nodes []*overlay.SelectedNode
var nodes []*nodeselection.SelectedNode
if len(args) == 0 {
err = db.OverlayCache().IterateAllContactedNodes(ctx, func(ctx context.Context, node *overlay.SelectedNode) error {
err = db.OverlayCache().IterateAllContactedNodes(ctx, func(ctx context.Context, node *nodeselection.SelectedNode) error {
nodes = append(nodes, node)
return nil
})
@ -985,7 +1017,7 @@ func cmdRestoreTrash(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
nodes = append(nodes, &overlay.SelectedNode{
nodes = append(nodes, &nodeselection.SelectedNode{
ID: dossier.Id,
Address: dossier.Address,
LastNet: dossier.LastNet,

View File

@ -94,7 +94,12 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
dialer := rpc.NewDefaultDialer(tlsOptions)
overlay, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
placement, err := config.Placement.Parse()
if err != nil {
return err
}
overlayService, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
if err != nil {
return err
}
@ -102,8 +107,9 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
orders, err := orders.NewService(
log.Named("orders"),
signing.SignerFromFullIdentity(identity),
overlay,
overlayService,
orders.NewNoopDB(),
placement.CreateFilters,
config.Orders,
)
if err != nil {
@ -122,9 +128,10 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
log.Named("segment-repair"),
metabaseDB,
orders,
overlay,
overlayService,
nil, // TODO add noop version
ecRepairer,
placement.CreateFilters,
config.Checker.RepairOverrides,
config.Repairer,
)
@ -132,7 +139,7 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
// TODO reorganize to avoid using peer.
peer := &satellite.Repairer{}
peer.Overlay = overlay
peer.Overlay = overlayService
peer.Orders.Service = orders
peer.EcRepairer = ecRepairer
peer.SegmentRepairer = segmentRepairer
@ -274,10 +281,8 @@ func reuploadSegment(ctx context.Context, log *zap.Logger, peer *satellite.Repai
return errs.New("not enough new nodes were found for repair: min %v got %v", redundancy.RepairThreshold(), len(newNodes))
}
optimalThresholdMultiplier := float64(1) // is this value fine?
numHealthyInExcludedCountries := 0
putLimits, putPrivateKey, err := peer.Orders.Service.CreatePutRepairOrderLimits(ctx, segment, make([]*pb.AddressedOrderLimit, len(newNodes)),
make(map[int32]struct{}), newNodes, optimalThresholdMultiplier, numHealthyInExcludedCountries)
make(map[uint16]struct{}), newNodes)
if err != nil {
return errs.New("could not create PUT_REPAIR order limits: %w", err)
}

47
cmd/satellite/ui.go Normal file
View File

@ -0,0 +1,47 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"github.com/spf13/cobra"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/private/process"
"storj.io/storj/satellite"
)
func cmdUIRun(cmd *cobra.Command, args []string) (err error) {
ctx, _ := process.Ctx(cmd)
log := zap.L()
runCfg.Debug.Address = *process.DebugAddrFlag
identity, err := runCfg.Identity.Load()
if err != nil {
log.Error("Failed to load identity.", zap.Error(err))
return errs.New("Failed to load identity: %+v", err)
}
satAddr := runCfg.Config.Contact.ExternalAddress
if satAddr == "" {
return errs.New("cannot run satellite ui if contact.external-address is not set")
}
apiAddress := runCfg.Config.Console.ExternalAddress
if apiAddress == "" {
apiAddress = runCfg.Config.Console.Address
}
peer, err := satellite.NewUI(log, identity, &runCfg.Config, process.AtomicLevel(cmd), satAddr, apiAddress)
if err != nil {
return err
}
if err := process.InitMetricsWithHostname(ctx, log, nil); err != nil {
log.Warn("Failed to initialize telemetry batcher on satellite api", zap.Error(err))
}
runError := peer.Run(ctx)
closeError := peer.Close()
return errs.Combine(runError, closeError)
}

View File

@ -0,0 +1,242 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"github.com/spf13/cobra"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/common/storj"
"storj.io/private/cfgstruct"
"storj.io/private/process"
"storj.io/storj/storagenode"
"storj.io/storj/storagenode/pieces"
"storj.io/storj/storagenode/satellites"
"storj.io/storj/storagenode/storagenodedb"
"storj.io/storj/storagenode/trust"
)
// runCfg defines configuration for run command.
type forgetSatelliteCfg struct {
storagenode.Config
SatelliteIDs []string `internal:"true"`
AllUntrusted bool `help:"Clean up all untrusted satellites" default:"false"`
Force bool `help:"Force removal of satellite data if not listed in satelliteDB cache or marked as untrusted" default:"false"`
}
func newForgetSatelliteCmd(f *Factory) *cobra.Command {
var cfg forgetSatelliteCfg
cmd := &cobra.Command{
Use: "forget-satellite [satellite_IDs...]",
Short: "Remove an untrusted satellite from the trust cache and clean up its data",
Long: "Forget a satellite.\n" +
"The command shows the list of the available untrusted satellites " +
"and removes the selected satellites from the trust cache and clean up the available data",
Example: `
# Specify satellite ID to forget
$ storagenode forget-satellite --identity-dir /path/to/identityDir --config-dir /path/to/configDir satellite_ID
# Specify multiple satellite IDs to forget
$ storagenode forget-satellite satellite_ID1 satellite_ID2 --identity-dir /path/to/identityDir --config-dir /path/to/configDir
# Clean up all untrusted satellites
# This checks for untrusted satellites in both the satelliteDB cache and the excluded satellites list
# specified in the config.yaml file
$ storagenode forget-satellite --all-untrusted --identity-dir /path/to/identityDir --config-dir /path/to/configDir
# For force removal of data for untrusted satellites that are not listed in satelliteDB cache or marked as untrusted
$ storagenode forget-satellite satellite_ID1 satellite_ID2 --force --identity-dir /path/to/identityDir --config-dir /path/to/configDir
`,
RunE: func(cmd *cobra.Command, args []string) error {
cfg.SatelliteIDs = args
if len(args) > 0 && cfg.AllUntrusted {
return errs.New("cannot specify both satellite IDs and --all-untrusted")
}
if len(args) == 0 && !cfg.AllUntrusted {
return errs.New("must specify either satellite ID(s) as arguments or --all-untrusted flag")
}
if cfg.AllUntrusted && cfg.Force {
return errs.New("cannot specify both --all-untrusted and --force")
}
ctx, _ := process.Ctx(cmd)
return cmdForgetSatellite(ctx, zap.L(), &cfg)
},
Annotations: map[string]string{"type": "helper"},
}
process.Bind(cmd, &cfg, f.Defaults, cfgstruct.ConfDir(f.ConfDir), cfgstruct.IdentityDir(f.IdentityDir))
return cmd
}
func cmdForgetSatellite(ctx context.Context, log *zap.Logger, cfg *forgetSatelliteCfg) (err error) {
// we don't really need the identity, but we load it as a sanity check
ident, err := cfg.Identity.Load()
if err != nil {
log.Fatal("Failed to load identity.", zap.Error(err))
} else {
log.Info("Identity loaded.", zap.Stringer("Node ID", ident.ID))
}
db, err := storagenodedb.OpenExisting(ctx, log.Named("db"), cfg.DatabaseConfig())
if err != nil {
return errs.New("Error starting master database on storagenode: %+v", err)
}
satelliteDB := db.Satellites()
// get list of excluded satellites
excludedSatellites := make(map[storj.NodeID]bool)
for _, rule := range cfg.Storage2.Trust.Exclusions.Rules {
url, err := trust.ParseSatelliteURL(rule.String())
if err != nil {
log.Warn("Failed to parse satellite URL from exclusions list", zap.Error(err), zap.String("rule", rule.String()))
continue
}
excludedSatellites[url.ID] = false // false means the satellite has not been cleaned up yet.
}
if len(cfg.SatelliteIDs) > 0 {
for _, satelliteIDStr := range cfg.SatelliteIDs {
satelliteID, err := storj.NodeIDFromString(satelliteIDStr)
if err != nil {
return err
}
satellite := satellites.Satellite{
SatelliteID: satelliteID,
Status: satellites.Untrusted,
}
// check if satellite is excluded
cleanedUp, isExcluded := excludedSatellites[satelliteID]
if !isExcluded {
sat, err := satelliteDB.GetSatellite(ctx, satelliteID)
if err != nil {
return err
}
if !satellite.SatelliteID.IsZero() {
satellite = sat
}
if satellite.SatelliteID.IsZero() && !cfg.Force {
return errs.New("satellite %v not found. Specify --force to force data deletion", satelliteID)
}
log.Warn("Satellite not found in satelliteDB cache. Forcing removal of satellite data.", zap.Stringer("satelliteID", satelliteID))
}
if cleanedUp {
log.Warn("Satellite already cleaned up", zap.Stringer("satelliteID", satelliteID))
continue
}
err = cleanupSatellite(ctx, log, cfg, db, satellite)
if err != nil {
return err
}
}
} else {
sats, err := satelliteDB.GetSatellites(ctx)
if err != nil {
return err
}
hasUntrusted := false
for _, satellite := range sats {
if satellite.Status != satellites.Untrusted {
continue
}
hasUntrusted = true
err = cleanupSatellite(ctx, log, cfg, db, satellite)
if err != nil {
return err
}
excludedSatellites[satellite.SatelliteID] = true // true means the satellite has been cleaned up.
}
// clean up excluded satellites that might not be in the satelliteDB cache.
for satelliteID, cleanedUp := range excludedSatellites {
if !cleanedUp {
satellite := satellites.Satellite{
SatelliteID: satelliteID,
Status: satellites.Untrusted,
}
hasUntrusted = true
err = cleanupSatellite(ctx, log, cfg, db, satellite)
if err != nil {
return err
}
}
}
if !hasUntrusted {
log.Info("No untrusted satellites found. You can add satellites to the exclusions list in the config.yaml file.")
}
}
return nil
}
func cleanupSatellite(ctx context.Context, log *zap.Logger, cfg *forgetSatelliteCfg, db *storagenodedb.DB, satellite satellites.Satellite) error {
if satellite.Status != satellites.Untrusted && !cfg.Force {
log.Error("Satellite is not untrusted. Skipping", zap.Stringer("satelliteID", satellite.SatelliteID))
return nil
}
log.Info("Removing satellite from trust cache.", zap.Stringer("satelliteID", satellite.SatelliteID))
cache, err := trust.LoadCache(cfg.Storage2.Trust.CachePath)
if err != nil {
return err
}
deleted := cache.DeleteSatelliteEntry(satellite.SatelliteID)
if deleted {
if err := cache.Save(ctx); err != nil {
return err
}
log.Info("Satellite removed from trust cache.", zap.Stringer("satelliteID", satellite.SatelliteID))
}
log.Info("Cleaning up satellite data.", zap.Stringer("satelliteID", satellite.SatelliteID))
blobs := pieces.NewBlobsUsageCache(log.Named("blobscache"), db.Pieces())
if err := blobs.DeleteNamespace(ctx, satellite.SatelliteID.Bytes()); err != nil {
return err
}
log.Info("Cleaning up the trash.", zap.Stringer("satelliteID", satellite.SatelliteID))
err = blobs.DeleteTrashNamespace(ctx, satellite.SatelliteID.Bytes())
if err != nil {
return err
}
log.Info("Removing satellite info from reputation DB.", zap.Stringer("satelliteID", satellite.SatelliteID))
err = db.Reputation().Delete(ctx, satellite.SatelliteID)
if err != nil {
return err
}
// delete v0 pieces for the satellite, if any.
log.Info("Removing satellite v0 pieces if any.", zap.Stringer("satelliteID", satellite.SatelliteID))
err = db.V0PieceInfo().WalkSatelliteV0Pieces(ctx, db.Pieces(), satellite.SatelliteID, func(access pieces.StoredPieceAccess) error {
return db.Pieces().Delete(ctx, access.BlobRef())
})
if err != nil {
return err
}
log.Info("Removing satellite from satellites DB.", zap.Stringer("satelliteID", satellite.SatelliteID))
err = db.Satellites().DeleteSatellite(ctx, satellite.SatelliteID)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,242 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/zeebo/errs"
"go.uber.org/zap/zaptest"
"storj.io/common/identity"
"storj.io/common/memory"
"storj.io/common/testcontext"
"storj.io/common/testrand"
"storj.io/storj/private/testplanet"
"storj.io/storj/storagenode/blobstore"
"storj.io/storj/storagenode/blobstore/filestore"
"storj.io/storj/storagenode/reputation"
"storj.io/storj/storagenode/satellites"
)
func Test_newForgetSatelliteCmd_Error(t *testing.T) {
tests := []struct {
name string
args string
wantErr string
}{
{
name: "no args",
args: "",
wantErr: "must specify either satellite ID(s) as arguments or --all-untrusted flag",
},
{
name: "Both satellite ID and --all-untrusted flag specified",
args: "--all-untrusted 1234567890123456789012345678901234567890123456789012345678901234",
wantErr: "cannot specify both satellite IDs and --all-untrusted",
},
{
name: "--all-untrusted and --force specified",
args: "--all-untrusted --force",
wantErr: "cannot specify both --all-untrusted and --force",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := newForgetSatelliteCmd(&Factory{})
cmd.SetArgs(strings.Fields(tt.args))
err := cmd.ExecuteContext(testcontext.New(t))
if tt.wantErr == "" {
require.NoError(t, err)
return
}
require.Equal(t, tt.wantErr, err.Error())
})
}
}
func Test_cmdForgetSatellite(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 2, StorageNodeCount: 1, UplinkCount: 0,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
address := planet.StorageNodes[0].Server.PrivateAddr().String()
db := planet.StorageNodes[0].DB
log := zaptest.NewLogger(t)
store, err := filestore.NewAt(log, db.Config().Pieces, filestore.DefaultConfig)
require.NoError(t, err)
satelliteID := planet.Satellites[0].ID()
blobSize := memory.KB
blobRef := blobstore.BlobRef{
Namespace: satelliteID.Bytes(),
Key: testrand.PieceID().Bytes(),
}
w, err := store.Create(ctx, blobRef, -1)
require.NoError(t, err)
_, err = w.Write(testrand.Bytes(blobSize))
require.NoError(t, err)
require.NoError(t, w.Commit(ctx))
// create a new satellite reputation
timestamp := time.Now().UTC()
reputationDB := db.Reputation()
stats := reputation.Stats{
SatelliteID: satelliteID,
Audit: reputation.Metric{
TotalCount: 6,
SuccessCount: 7,
Alpha: 8,
Beta: 9,
Score: 10,
UnknownAlpha: 11,
UnknownBeta: 12,
UnknownScore: 13,
},
OnlineScore: 14,
UpdatedAt: timestamp,
JoinedAt: timestamp,
}
err = reputationDB.Store(ctx, stats)
require.NoError(t, err)
// test that the reputation was stored correctly
rstats, err := reputationDB.Get(ctx, satelliteID)
require.NoError(t, err)
require.NotNil(t, rstats)
require.Equal(t, stats, *rstats)
// insert a new untrusted satellite in the database
err = db.Satellites().SetAddressAndStatus(ctx, satelliteID, address, satellites.Untrusted)
require.NoError(t, err)
// test that the satellite was inserted correctly
satellite, err := db.Satellites().GetSatellite(ctx, satelliteID)
require.NoError(t, err)
require.Equal(t, satellites.Untrusted, satellites.Status(satellite.Status))
// set up the identity
ident := planet.StorageNodes[0].Identity
identConfig := identity.Config{
CertPath: ctx.File("identity", "identity.cert"),
KeyPath: ctx.File("identity", "identity.Key"),
}
err = identConfig.Save(ident)
require.NoError(t, err)
planet.StorageNodes[0].Config.Identity = identConfig
// run the forget satellite command with All flag
err = cmdForgetSatellite(ctx, log, &forgetSatelliteCfg{
AllUntrusted: true,
Config: planet.StorageNodes[0].Config,
})
require.NoError(t, err)
// check that the blob was deleted
blobInfo, err := store.Stat(ctx, blobRef)
require.Error(t, err)
require.True(t, errs.Is(err, os.ErrNotExist))
require.Nil(t, blobInfo)
// check that the reputation was deleted
rstats, err = reputationDB.Get(ctx, satelliteID)
require.NoError(t, err)
require.Equal(t, &reputation.Stats{SatelliteID: satelliteID}, rstats)
// check that the satellite info was deleted from the database
satellite, err = db.Satellites().GetSatellite(ctx, satelliteID)
require.NoError(t, err)
require.True(t, satellite.SatelliteID.IsZero())
})
}
func Test_cmdForgetSatellite_Exclusions(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 2, StorageNodeCount: 1, UplinkCount: 0,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
address := planet.StorageNodes[0].Server.PrivateAddr().String()
db := planet.StorageNodes[0].DB
log := zaptest.NewLogger(t)
store, err := filestore.NewAt(log, db.Config().Pieces, filestore.DefaultConfig)
require.NoError(t, err)
satelliteID := planet.Satellites[0].ID()
blobSize := memory.KB
blobRef := blobstore.BlobRef{
Namespace: satelliteID.Bytes(),
Key: testrand.PieceID().Bytes(),
}
w, err := store.Create(ctx, blobRef, -1)
require.NoError(t, err)
_, err = w.Write(testrand.Bytes(blobSize))
require.NoError(t, err)
require.NoError(t, w.Commit(ctx))
// create a new satellite reputation
timestamp := time.Now().UTC()
reputationDB := db.Reputation()
stats := reputation.Stats{
SatelliteID: satelliteID,
Audit: reputation.Metric{
TotalCount: 6,
SuccessCount: 7,
Alpha: 8,
Beta: 9,
Score: 10,
UnknownAlpha: 11,
UnknownBeta: 12,
UnknownScore: 13,
},
OnlineScore: 14,
UpdatedAt: timestamp,
JoinedAt: timestamp,
}
err = reputationDB.Store(ctx, stats)
require.NoError(t, err)
// test that the reputation was stored correctly
rstats, err := reputationDB.Get(ctx, satelliteID)
require.NoError(t, err)
require.NotNil(t, rstats)
require.Equal(t, stats, *rstats)
// set up the identity
ident := planet.StorageNodes[0].Identity
identConfig := identity.Config{
CertPath: ctx.File("identity", "identity.cert"),
KeyPath: ctx.File("identity", "identity.Key"),
}
err = identConfig.Save(ident)
require.NoError(t, err)
planet.StorageNodes[0].Config.Identity = identConfig
// add the satellite to the exclusion list
err = planet.StorageNodes[0].Config.Storage2.Trust.Exclusions.Set(satelliteID.String() + "@" + address)
require.NoError(t, err)
// run the forget satellite command with All flag
err = cmdForgetSatellite(ctx, log, &forgetSatelliteCfg{
AllUntrusted: true,
Config: planet.StorageNodes[0].Config,
})
require.NoError(t, err)
// check that the blob was deleted
blobInfo, err := store.Stat(ctx, blobRef)
require.Error(t, err)
require.True(t, errs.Is(err, os.ErrNotExist))
require.Nil(t, blobInfo)
// check that the reputation was deleted
rstats, err = reputationDB.Get(ctx, satelliteID)
require.NoError(t, err)
require.Equal(t, &reputation.Stats{SatelliteID: satelliteID}, rstats)
// check that the satellite info was deleted from the database
satellite, err := db.Satellites().GetSatellite(ctx, satelliteID)
require.NoError(t, err)
require.True(t, satellite.SatelliteID.IsZero())
})
}

View File

@ -59,6 +59,7 @@ func newRootCmd(setDefaults bool) (*cobra.Command, *Factory) {
newIssueAPIKeyCmd(factory),
newGracefulExitInitCmd(factory),
newGracefulExitStatusCmd(factory),
newForgetSatelliteCmd(factory),
// internal hidden commands
internalcmd.NewUsedSpaceFilewalkerCmd().Command,
internalcmd.NewGCFilewalkerCmd().Command,

View File

@ -65,11 +65,15 @@ func (ce *consoleEndpoints) Token() string {
return ce.appendPath("/api/v0/auth/token")
}
func (ce *consoleEndpoints) GraphQL() string {
return ce.appendPath("/api/v0/graphql")
func (ce *consoleEndpoints) Projects() string {
return ce.appendPath("/api/v0/projects")
}
func (ce *consoleEndpoints) graphqlDo(request *http.Request, jsonResponse interface{}) error {
func (ce *consoleEndpoints) APIKeys() string {
return ce.appendPath("/api/v0/api-keys")
}
func (ce *consoleEndpoints) httpDo(request *http.Request, jsonResponse interface{}) error {
resp, err := ce.client.Do(request)
if err != nil {
return err
@ -81,24 +85,24 @@ func (ce *consoleEndpoints) graphqlDo(request *http.Request, jsonResponse interf
return err
}
var response struct {
Data json.RawMessage
Errors []interface{}
}
if err = json.NewDecoder(bytes.NewReader(b)).Decode(&response); err != nil {
return err
}
if response.Errors != nil {
return errs.New("inner graphql error: %v", response.Errors)
}
if jsonResponse == nil {
return errs.New("empty response: %q", b)
}
return json.NewDecoder(bytes.NewReader(response.Data)).Decode(jsonResponse)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return json.NewDecoder(bytes.NewReader(b)).Decode(jsonResponse)
}
var errResponse struct {
Error string `json:"error"`
}
err = json.NewDecoder(bytes.NewReader(b)).Decode(&errResponse)
if err != nil {
return err
}
return errs.New("request failed with status %d: %s", resp.StatusCode, errResponse.Error)
}
func (ce *consoleEndpoints) createOrGetAPIKey(ctx context.Context) (string, error) {
@ -464,49 +468,41 @@ func (ce *consoleEndpoints) getProject(ctx context.Context, token string) (strin
request, err := http.NewRequestWithContext(
ctx,
http.MethodGet,
ce.GraphQL(),
ce.Projects(),
nil)
if err != nil {
return "", errs.Wrap(err)
}
q := request.URL.Query()
q.Add("query", `query {myProjects{id}}`)
request.URL.RawQuery = q.Encode()
request.AddCookie(&http.Cookie{
Name: ce.cookieName,
Value: token,
})
request.Header.Add("Content-Type", "application/graphql")
request.Header.Add("Content-Type", "application/json")
var getProjects struct {
MyProjects []struct {
ID string
var projects []struct {
ID string `json:"id"`
}
}
if err := ce.graphqlDo(request, &getProjects); err != nil {
if err := ce.httpDo(request, &projects); err != nil {
return "", errs.Wrap(err)
}
if len(getProjects.MyProjects) == 0 {
if len(projects) == 0 {
return "", errs.New("no projects")
}
return getProjects.MyProjects[0].ID, nil
return projects[0].ID, nil
}
func (ce *consoleEndpoints) createProject(ctx context.Context, token string) (string, error) {
rng := rand.NewSource(time.Now().UnixNano())
createProjectQuery := fmt.Sprintf(
`mutation {createProject(input:{name:"TestProject-%d",description:""}){id}}`,
rng.Int63())
body := fmt.Sprintf(`{"name":"TestProject-%d","description":""}`, rng.Int63())
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
ce.GraphQL(),
bytes.NewReader([]byte(createProjectQuery)))
ce.Projects(),
bytes.NewReader([]byte(body)))
if err != nil {
return "", errs.Wrap(err)
}
@ -516,31 +512,27 @@ func (ce *consoleEndpoints) createProject(ctx context.Context, token string) (st
Value: token,
})
request.Header.Add("Content-Type", "application/graphql")
request.Header.Add("Content-Type", "application/json")
var createProject struct {
CreateProject struct {
ID string
var createdProject struct {
ID string `json:"id"`
}
}
if err := ce.graphqlDo(request, &createProject); err != nil {
if err := ce.httpDo(request, &createdProject); err != nil {
return "", errs.Wrap(err)
}
return createProject.CreateProject.ID, nil
return createdProject.ID, nil
}
func (ce *consoleEndpoints) createAPIKey(ctx context.Context, token, projectID string) (string, error) {
rng := rand.NewSource(time.Now().UnixNano())
createAPIKeyQuery := fmt.Sprintf(
`mutation {createAPIKey(projectID:%q,name:"TestKey-%d"){key}}`,
projectID, rng.Int63())
apiKeyName := fmt.Sprintf("TestKey-%d", rng.Int63())
request, err := http.NewRequestWithContext(
ctx,
http.MethodPost,
ce.GraphQL(),
bytes.NewReader([]byte(createAPIKeyQuery)))
ce.APIKeys()+"/create/"+projectID,
bytes.NewReader([]byte(apiKeyName)))
if err != nil {
return "", errs.Wrap(err)
}
@ -550,18 +542,16 @@ func (ce *consoleEndpoints) createAPIKey(ctx context.Context, token, projectID s
Value: token,
})
request.Header.Add("Content-Type", "application/graphql")
request.Header.Add("Content-Type", "application/json")
var createAPIKey struct {
CreateAPIKey struct {
Key string
var createdKey struct {
Key string `json:"key"`
}
}
if err := ce.graphqlDo(request, &createAPIKey); err != nil {
if err := ce.httpDo(request, &createdKey); err != nil {
return "", errs.Wrap(err)
}
return createAPIKey.CreateAPIKey.Key, nil
return createdKey.Key, nil
}
func generateActivationKey(userID uuid.UUID, email string, createdAt time.Time) (string, error) {

View File

@ -255,7 +255,8 @@ func (process *Process) Exec(ctx context.Context, command string) (err error) {
if _, ok := process.Arguments[command]; !ok {
fmt.Fprintf(process.processes.Output, "%s running: %s\n", process.Name, command)
return
//TODO: This doesn't look right, but keeping the same behaviour as before.
return nil
}
cmd := exec.CommandContext(ctx, executable, process.Arguments[command]...)

View File

@ -203,12 +203,12 @@ func verifySegments(cmd *cobra.Command, args []string) error {
dialer := rpc.NewDefaultDialer(tlsOptions)
// setup dependencies for verification
overlay, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), "", "", satelliteCfg.Overlay)
overlayService, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), overlay.NewPlacementDefinitions().CreateFilters, "", "", satelliteCfg.Overlay)
if err != nil {
return Error.Wrap(err)
}
ordersService, err := orders.NewService(log.Named("orders"), signing.SignerFromFullIdentity(identity), overlay, orders.NewNoopDB(), satelliteCfg.Orders)
ordersService, err := orders.NewService(log.Named("orders"), signing.SignerFromFullIdentity(identity), overlayService, orders.NewNoopDB(), overlay.NewPlacementDefinitions().CreateFilters, satelliteCfg.Orders)
if err != nil {
return Error.Wrap(err)
}
@ -243,7 +243,7 @@ func verifySegments(cmd *cobra.Command, args []string) error {
// setup verifier
verifier := NewVerifier(log.Named("verifier"), dialer, ordersService, verifyConfig)
service, err := NewService(log.Named("service"), metabaseDB, verifier, overlay, serviceConfig)
service, err := NewService(log.Named("service"), metabaseDB, verifier, overlayService, serviceConfig)
if err != nil {
return Error.Wrap(err)
}

View File

@ -15,6 +15,7 @@ import (
"storj.io/common/uuid"
"storj.io/private/process"
"storj.io/storj/satellite/metabase"
"storj.io/storj/satellite/nodeselection"
"storj.io/storj/satellite/overlay"
"storj.io/storj/satellite/satellitedb"
)
@ -78,7 +79,7 @@ type NodeCheckConfig struct {
// NodeCheckOverlayDB contains dependencies from overlay that are needed for the processing.
type NodeCheckOverlayDB interface {
IterateAllContactedNodes(context.Context, func(context.Context, *overlay.SelectedNode) error) error
IterateAllContactedNodes(context.Context, func(context.Context, *nodeselection.SelectedNode) error) error
IterateAllNodeDossiers(context.Context, func(context.Context, *overlay.NodeDossier) error) error
}

View File

@ -21,6 +21,7 @@ import (
"storj.io/common/uuid"
"storj.io/storj/satellite/audit"
"storj.io/storj/satellite/metabase"
"storj.io/storj/satellite/nodeselection"
"storj.io/storj/satellite/overlay"
)
@ -46,7 +47,7 @@ type Verifier interface {
type Overlay interface {
// Get looks up the node by nodeID
Get(ctx context.Context, nodeID storj.NodeID) (*overlay.NodeDossier, error)
SelectAllStorageNodesDownload(ctx context.Context, onlineWindow time.Duration, asOf overlay.AsOfSystemTimeConfig) ([]*overlay.SelectedNode, error)
SelectAllStorageNodesDownload(ctx context.Context, onlineWindow time.Duration, asOf overlay.AsOfSystemTimeConfig) ([]*nodeselection.SelectedNode, error)
}
// SegmentWriter allows writing segments to some output.

View File

@ -23,6 +23,7 @@ import (
segmentverify "storj.io/storj/cmd/tools/segment-verify"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite/metabase"
"storj.io/storj/satellite/nodeselection"
"storj.io/storj/satellite/overlay"
)
@ -344,10 +345,10 @@ func (db *metabaseMock) Get(ctx context.Context, nodeID storj.NodeID) (*overlay.
}, nil
}
func (db *metabaseMock) SelectAllStorageNodesDownload(ctx context.Context, onlineWindow time.Duration, asOf overlay.AsOfSystemTimeConfig) ([]*overlay.SelectedNode, error) {
var xs []*overlay.SelectedNode
func (db *metabaseMock) SelectAllStorageNodesDownload(ctx context.Context, onlineWindow time.Duration, asOf overlay.AsOfSystemTimeConfig) ([]*nodeselection.SelectedNode, error) {
var xs []*nodeselection.SelectedNode
for nodeID := range db.nodeIDToAlias {
xs = append(xs, &overlay.SelectedNode{
xs = append(xs, &nodeselection.SelectedNode{
ID: nodeID,
Address: &pb.NodeAddress{
Address: fmt.Sprintf("nodeid:%v", nodeID),

View File

@ -0,0 +1,201 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/gogo/protobuf/proto"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/common/identity"
"storj.io/common/nodetag"
"storj.io/common/pb"
"storj.io/common/signing"
"storj.io/common/storj"
"storj.io/private/process"
)
var (
rootCmd = &cobra.Command{
Use: "tag-signer",
Short: "Sign key=value pairs with identity",
Long: "Node tags are arbitrary key value pairs signed by an authority. If the public key is configured on " +
"Satellite side, Satellite will check the signatures and save the tags, which can be used (for example)" +
" during node selection. Storagenodes can be configured to send encoded node tags to the Satellite. " +
"This utility helps creating/managing the values of this specific configuration value, which is encoded by default.",
}
signCmd = &cobra.Command{
Use: "sign <key=value> <key2=value> ...",
Short: "Create signed tagset",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, _ := process.Ctx(cmd)
encoded, err := signTags(ctx, config, args)
if err != nil {
return err
}
fmt.Println(encoded)
return nil
},
}
inspectCmd = &cobra.Command{
Use: "inspect <encoded string>",
Short: "Print out the details from an encoded node set",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx, _ := process.Ctx(cmd)
return inspect(ctx, args[0])
},
}
config Config
)
// Config contains configuration required for signing.
type Config struct {
IdentityDir string `help:"location if the identity files" path:"true"`
NodeID string `help:"the ID of the node, which will used this tag "`
}
func init() {
rootCmd.AddCommand(signCmd)
rootCmd.AddCommand(inspectCmd)
process.Bind(signCmd, &config)
}
func signTags(ctx context.Context, cfg Config, tagPairs []string) (string, error) {
if cfg.IdentityDir == "" {
return "", errs.New("Please specify the identity, used as a signer with --identity-dir")
}
if cfg.NodeID == "" {
return "", errs.New("Please specify the --node-id")
}
identityConfig := identity.Config{
CertPath: filepath.Join(cfg.IdentityDir, "identity.cert"),
KeyPath: filepath.Join(cfg.IdentityDir, "identity.key"),
}
fullIdentity, err := identityConfig.Load()
if err != nil {
return "", err
}
signer := signing.SignerFromFullIdentity(fullIdentity)
nodeID, err := storj.NodeIDFromString(cfg.NodeID)
if err != nil {
return "", errs.New("Wrong NodeID format: %v", err)
}
tagSet := &pb.NodeTagSet{
NodeId: nodeID.Bytes(),
SignedAt: time.Now().Unix(),
}
for _, tag := range tagPairs {
tag = strings.TrimSpace(tag)
if len(tag) == 0 {
continue
}
parts := strings.SplitN(tag, "=", 2)
if len(parts) != 2 {
return "", errs.New("tags should be in KEY=VALUE format, but it was %s", tag)
}
tagSet.Tags = append(tagSet.Tags, &pb.Tag{
Name: parts[0],
Value: []byte(parts[1]),
})
}
signedMessage, err := nodetag.Sign(ctx, tagSet, signer)
if err != nil {
return "", err
}
all := &pb.SignedNodeTagSets{
Tags: []*pb.SignedNodeTagSet{
signedMessage,
},
}
raw, err := proto.Marshal(all)
if err != nil {
return "", errs.Wrap(err)
}
return base64.StdEncoding.EncodeToString(raw), nil
}
func inspect(ctx context.Context, s string) error {
raw, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return errs.New("Input is not in base64 format")
}
sets := &pb.SignedNodeTagSets{}
err = proto.Unmarshal(raw, sets)
if err != nil {
return errs.New("Input is not a protobuf encoded *pb.SignedNodeTagSets message")
}
for _, msg := range sets.Tags {
signerNodeID, err := storj.NodeIDFromBytes(msg.SignerNodeId)
if err != nil {
return err
}
fmt.Println("Signer: ", signerNodeID.String())
fmt.Println("Signature: ", hex.EncodeToString(msg.Signature))
tags := &pb.NodeTagSet{}
err = proto.Unmarshal(msg.SerializedTag, tags)
if err != nil {
return err
}
nodeID, err := storj.NodeIDFromBytes(tags.NodeId)
if err != nil {
return err
}
fmt.Println("SignedAt: ", time.Unix(tags.SignedAt, 0).Format(time.RFC3339))
fmt.Println("NodeID: ", nodeID.String())
fmt.Println("Tags:")
for _, tag := range tags.Tags {
fmt.Printf(" %s=%s\n", tag.Name, string(tag.Value))
}
fmt.Println()
}
return nil
}
func main() {
process.ExecWithCustomOptions(rootCmd, process.ExecOptions{
LoadConfig: func(cmd *cobra.Command, vip *viper.Viper) error {
return nil
},
InitTracing: false,
LoggerFactory: func(logger *zap.Logger) *zap.Logger {
newLogger, level, err := process.NewLogger("tag-signer")
if err != nil {
panic(err)
}
level.SetLevel(zap.WarnLevel)
return newLogger
},
})
}

View File

@ -29,6 +29,8 @@ type accessPermissions struct {
notBefore *time.Time
notAfter *time.Time
maxObjectTTL *time.Duration
}
func (ap *accessPermissions) Setup(params clingy.Parameters, prefixFlags bool) {
@ -65,6 +67,12 @@ func (ap *accessPermissions) Setup(params clingy.Parameters, prefixFlags bool) {
"Disallow access after this time (e.g. '+2h', 'now', '2020-01-02T15:04:05Z0700', 'none')",
nil, clingy.Transform(parseHumanDateNotAfter), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
params.Break()
ap.maxObjectTTL = params.Flag("max-object-ttl",
"The object is automatically deleted after this period. (e.g. '1h30m', '24h', '720h')",
nil, clingy.Transform(time.ParseDuration), clingy.Type("period"), clingy.Optional).(*time.Duration)
if !prefixFlags {
ap.prefixes = params.Arg("prefix", "Key prefix access will be restricted to",
clingy.Transform(ulloc.Parse),
@ -93,6 +101,7 @@ func (ap *accessPermissions) Apply(access *uplink.Access) (*uplink.Access, error
AllowUpload: ap.AllowUpload(),
NotBefore: ap.NotBefore(),
NotAfter: ap.NotAfter(),
MaxObjectTTL: ap.MaxObjectTTL(),
}
// if we aren't actually restricting anything, then we don't need to Share.
@ -126,3 +135,4 @@ func (ap *accessPermissions) AllowDelete() bool { return !defaulted(ap.disall
func (ap *accessPermissions) AllowList() bool { return !defaulted(ap.disallowLists, ap.writeonly) }
func (ap *accessPermissions) AllowDownload() bool { return !defaulted(ap.disallowReads, ap.writeonly) }
func (ap *accessPermissions) AllowUpload() bool { return !defaulted(ap.disallowWrites, ap.readonly) }
func (ap *accessPermissions) MaxObjectTTL() *time.Duration { return ap.maxObjectTTL }

View File

@ -8,7 +8,6 @@ import (
"fmt"
"github.com/zeebo/clingy"
"github.com/zeebo/errs"
"storj.io/storj/cmd/uplink/ulext"
)
@ -33,7 +32,7 @@ func (c *cmdAccessUse) Execute(ctx context.Context) error {
return err
}
if _, ok := accesses[c.access]; !ok {
return errs.New("unknown access: %q", c.access)
return fmt.Errorf("ERROR: access %q does not exist. Use 'uplink access list' to see existing accesses", c.access)
}
if err := c.ex.SaveAccessInfo(c.access, accesses); err != nil {
return err

View File

@ -85,8 +85,7 @@ func (c *cmdCp) Setup(params clingy.Parameters) {
).(bool)
c.byteRange = params.Flag("range", "Downloads the specified range bytes of an object. For more information about the HTTP Range header, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35", "").(string)
parallelism := params.Flag("parallelism", "Controls how many parallel chunks to upload/download from a file", nil,
clingy.Optional,
c.parallelism = params.Flag("parallelism", "Controls how many parallel parts to upload/download from a file", 1,
clingy.Short('p'),
clingy.Transform(strconv.Atoi),
clingy.Transform(func(n int) (int, error) {
@ -95,8 +94,8 @@ func (c *cmdCp) Setup(params clingy.Parameters) {
}
return n, nil
}),
).(*int)
c.parallelismChunkSize = params.Flag("parallelism-chunk-size", "Set the size of the chunks for parallelism, 0 means automatic adjustment", memory.Size(0),
).(int)
c.parallelismChunkSize = params.Flag("parallelism-chunk-size", "Set the size of the parts for parallelism, 0 means automatic adjustment", memory.Size(0),
clingy.Transform(memory.ParseString),
clingy.Transform(func(n int64) (memory.Size, error) {
if n < 0 {
@ -107,17 +106,16 @@ func (c *cmdCp) Setup(params clingy.Parameters) {
).(memory.Size)
c.uploadConfig = testuplink.DefaultConcurrentSegmentUploadsConfig()
maxConcurrent := params.Flag(
c.uploadConfig.SchedulerOptions.MaximumConcurrent = params.Flag(
"maximum-concurrent-pieces",
"Maximum concurrent pieces to upload at once per transfer",
nil,
clingy.Optional,
"Maximum concurrent pieces to upload at once per part",
c.uploadConfig.SchedulerOptions.MaximumConcurrent,
clingy.Transform(strconv.Atoi),
clingy.Advanced,
).(*int)
).(int)
c.uploadConfig.SchedulerOptions.MaximumConcurrentHandles = params.Flag(
"maximum-concurrent-segments",
"Maximum concurrent segments to upload at once per transfer",
"Maximum concurrent segments to upload at once per part",
c.uploadConfig.SchedulerOptions.MaximumConcurrentHandles,
clingy.Transform(strconv.Atoi),
clingy.Advanced,
@ -133,28 +131,6 @@ func (c *cmdCp) Setup(params clingy.Parameters) {
clingy.Advanced,
).(string)
{ // handle backwards compatibility around parallelism and maximum concurrent pieces
addr := func(x int) *int { return &x }
switch {
// if neither are actively set, use defaults
case parallelism == nil && maxConcurrent == nil:
parallelism = addr(1)
maxConcurrent = addr(c.uploadConfig.SchedulerOptions.MaximumConcurrent)
// if parallelism is not set, use a value based on maxConcurrent
case parallelism == nil:
parallelism = addr((*maxConcurrent + 99) / 100)
// if maxConcurrent is not set, use a value based on parallelism
case maxConcurrent == nil:
maxConcurrent = addr(100 * *parallelism)
}
c.uploadConfig.SchedulerOptions.MaximumConcurrent = *maxConcurrent
c.parallelism = *parallelism
}
c.inmemoryEC = params.Flag("inmemory-erasure-coding", "Keep erasure-coded pieces in-memory instead of writing them on the disk during upload", false,
clingy.Transform(strconv.ParseBool),
clingy.Boolean,
@ -194,9 +170,10 @@ func (c *cmdCp) Execute(ctx context.Context) error {
fs, err := c.ex.OpenFilesystem(ctx, c.access,
ulext.ConcurrentSegmentUploadsConfig(c.uploadConfig),
ulext.ConnectionPoolOptions(rpcpool.Options{
// Add a bit more capacity for connections to the satellite
Capacity: c.uploadConfig.SchedulerOptions.MaximumConcurrent + 5,
KeyCapacity: 5,
// Allow at least as many connections as the maximum concurrent pieces per
// parallel part per transfer, plus a few extra for the satellite.
Capacity: c.transfers*c.parallelism*c.uploadConfig.SchedulerOptions.MaximumConcurrent + 5,
KeyCapacity: 2,
IdleExpiration: 2 * time.Minute,
}))
if err != nil {
@ -419,17 +396,6 @@ func (c *cmdCp) copyFile(ctx context.Context, fs ulfs.Filesystem, source, dest u
}
defer func() { _ = mwh.Abort(ctx) }()
// if we're uploading, do a single part of maximum size
if dest.Remote() {
return errs.Wrap(c.singleCopy(
ctx,
source, dest,
mrh, mwh,
offset, length,
bar,
))
}
partSize, err := c.calculatePartSize(mrh.Length(), c.parallelismChunkSize.Int64())
if err != nil {
return err
@ -448,13 +414,15 @@ func (c *cmdCp) copyFile(ctx context.Context, fs ulfs.Filesystem, source, dest u
// calculatePartSize returns the needed part size in order to upload the file with size of 'length'.
// It hereby respects if the client requests/prefers a certain size and only increases if needed.
func (c *cmdCp) calculatePartSize(length, preferredSize int64) (requiredSize int64, err error) {
segC := (length / maxPartCount / (memory.MiB * 64).Int64()) + 1
requiredSize = segC * (memory.MiB * 64).Int64()
segC := (length / maxPartCount / memory.GiB.Int64()) + 1
requiredSize = segC * memory.GiB.Int64()
switch {
case preferredSize == 0:
return requiredSize, nil
case requiredSize <= preferredSize:
return preferredSize, nil
case length < 0: // let the user pick their size if we don't have a length to know better
return preferredSize, nil
default:
return 0, errs.New(fmt.Sprintf("the specified chunk size %s is too small, requires %s or larger",
memory.FormatBytes(preferredSize), memory.FormatBytes(requiredSize)))
@ -535,8 +503,8 @@ func (c *cmdCp) parallelCopy(
}
var readBufs *ulfs.BytesPool
if p > 1 && chunkSize > 0 && (source.Std() || dest.Std()) {
// Create the read buffer pool only for uploads from stdin and downloads to stdout with parallelism > 1.
if p > 1 && chunkSize > 0 && source.Std() {
// Create the read buffer pool only for uploads from stdin with parallelism > 1.
readBufs = ulfs.NewBytesPool(int(chunkSize))
}
@ -557,6 +525,14 @@ func (c *cmdCp) parallelCopy(
break
}
if i == 0 && bar != nil {
info, err := src.Info(ctx)
if err == nil {
bar.SetTotal(info.ContentLength, false)
bar.EnableTriggerComplete()
}
}
wh, err := dst.NextPart(ctx, chunk)
if err != nil {
_ = rh.Close()
@ -578,12 +554,8 @@ func (c *cmdCp) parallelCopy(
var w io.Writer = wh
if bar != nil {
bar.SetTotal(rh.Info().ContentLength, false)
bar.EnableTriggerComplete()
pw := bar.ProxyWriter(w)
defer func() {
_ = pw.Close()
}()
defer func() { _ = pw.Close() }()
w = pw
}
@ -619,59 +591,6 @@ func (c *cmdCp) parallelCopy(
return errs.Wrap(combineErrs(es))
}
func (c *cmdCp) singleCopy(
ctx context.Context,
source, dest ulloc.Location,
src ulfs.MultiReadHandle,
dst ulfs.MultiWriteHandle,
offset, length int64,
bar *mpb.Bar) error {
if offset != 0 {
if err := src.SetOffset(offset); err != nil {
return err
}
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
rh, err := src.NextPart(ctx, length)
if err != nil {
return errs.Wrap(err)
}
defer func() { _ = rh.Close() }()
wh, err := dst.NextPart(ctx, length)
if err != nil {
return errs.Wrap(err)
}
defer func() { _ = wh.Abort() }()
var w io.Writer = wh
if bar != nil {
bar.SetTotal(rh.Info().ContentLength, false)
bar.EnableTriggerComplete()
pw := bar.ProxyWriter(w)
defer func() { _ = pw.Close() }()
w = pw
}
if _, err := sync2.Copy(ctx, w, rh); err != nil {
return errs.Wrap(err)
}
if err := wh.Commit(); err != nil {
return errs.Wrap(err)
}
if err := dst.Commit(ctx); err != nil {
return errs.Wrap(err)
}
return nil
}
func newProgressBar(progress *mpb.Progress, name string, which, total int) *mpb.Bar {
const counterFmt = " % .2f / % .2f"
const percentageFmt = "%.2f "

View File

@ -99,46 +99,51 @@ func TestCpDownload(t *testing.T) {
func TestCpPartSize(t *testing.T) {
c := newCmdCp(nil)
// 1GiB file, should return 64MiB
partSize, err := c.calculatePartSize(memory.GiB.Int64(), c.parallelismChunkSize.Int64())
// 10 GiB file, should return 1 GiB
partSize, err := c.calculatePartSize(10*memory.GiB.Int64(), c.parallelismChunkSize.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.MiB*64, partSize)
require.EqualValues(t, 1*memory.GiB, partSize)
// 640 GB file, should return 64MiB.
partSize, err = c.calculatePartSize(memory.GB.Int64()*640, c.parallelismChunkSize.Int64())
// 10000 GB file, should return 1 GiB.
partSize, err = c.calculatePartSize(10000*memory.GB.Int64(), c.parallelismChunkSize.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.MiB*64, partSize)
require.EqualValues(t, 1*memory.GiB, partSize)
// 640GiB file, should return 128MiB.
partSize, err = c.calculatePartSize(memory.GiB.Int64()*640, c.parallelismChunkSize.Int64())
// 10000 GiB file, should return 2 GiB.
partSize, err = c.calculatePartSize(10000*memory.GiB.Int64(), c.parallelismChunkSize.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.MiB*128, partSize)
require.EqualValues(t, 2*memory.GiB, partSize)
// 1TiB file, should return 128MiB.
partSize, err = c.calculatePartSize(memory.TiB.Int64(), c.parallelismChunkSize.Int64())
// 10 TiB file, should return 2 GiB.
partSize, err = c.calculatePartSize(10*memory.TiB.Int64(), c.parallelismChunkSize.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.MiB*128, partSize)
require.EqualValues(t, 2*memory.GiB, partSize)
// 1.3TiB file, should return 192MiB.
partSize, err = c.calculatePartSize(memory.GiB.Int64()*1300, c.parallelismChunkSize.Int64())
// 20001 GiB file, should return 3 GiB.
partSize, err = c.calculatePartSize(20001*memory.GiB.Int64(), c.parallelismChunkSize.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.MiB*192, partSize)
require.EqualValues(t, 3*memory.GiB, partSize)
// should return 1GiB as requested.
partSize, err = c.calculatePartSize(memory.GiB.Int64()*1300, memory.GiB.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.GiB, partSize)
// should return 192 MiB and error, since preferred is too low.
partSize, err = c.calculatePartSize(memory.GiB.Int64()*1300, memory.MiB.Int64())
// should return 1 GiB and error, since preferred is too low.
partSize, err = c.calculatePartSize(1300*memory.GiB.Int64(), memory.MiB.Int64())
require.Error(t, err)
require.Equal(t, "the specified chunk size 1.0 MiB is too small, requires 192.0 MiB or larger", err.Error())
require.Equal(t, "the specified chunk size 1.0 MiB is too small, requires 1.0 GiB or larger", err.Error())
require.Zero(t, partSize)
// negative length should return 64MiB part size
partSize, err = c.calculatePartSize(-1, c.parallelismChunkSize.Int64())
// negative length should return asked for amount
partSize, err = c.calculatePartSize(-1, 1*memory.GiB.Int64())
require.NoError(t, err)
require.EqualValues(t, memory.MiB*64, partSize)
require.EqualValues(t, 1*memory.GiB, partSize)
// negative length should return specified amount
partSize, err = c.calculatePartSize(-1, 100)
require.NoError(t, err)
require.EqualValues(t, 100, partSize)
}
func TestCpUpload(t *testing.T) {

View File

@ -110,6 +110,7 @@ func (c *cmdShare) Execute(ctx context.Context) error {
fmt.Fprintf(clingy.Stdout(ctx), "Deletes : %s\n", formatPermission(c.ap.AllowDelete()))
fmt.Fprintf(clingy.Stdout(ctx), "NotBefore : %s\n", formatTimeRestriction(c.ap.NotBefore()))
fmt.Fprintf(clingy.Stdout(ctx), "NotAfter : %s\n", formatTimeRestriction(c.ap.NotAfter()))
fmt.Fprintf(clingy.Stdout(ctx), "MaxObjectTTL : %s\n", formatDuration(c.ap.maxObjectTTL))
fmt.Fprintf(clingy.Stdout(ctx), "Paths : %s\n", formatPaths(c.ap.prefixes))
fmt.Fprintf(clingy.Stdout(ctx), "=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========\n")
fmt.Fprintf(clingy.Stdout(ctx), "Access : %s\n", newAccessData)
@ -182,6 +183,13 @@ func formatTimeRestriction(t time.Time) string {
return formatTime(true, t)
}
func formatDuration(d *time.Duration) string {
if d == nil {
return "Not set"
}
return d.String()
}
func formatPaths(sharePrefixes []uplink.SharePrefix) string {
if len(sharePrefixes) == 0 {
return "WARNING! The entire project is shared!"

View File

@ -39,6 +39,7 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
@ -57,6 +58,7 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
@ -75,6 +77,7 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
@ -93,6 +96,7 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
@ -122,13 +126,14 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
`)
})
t.Run("share access with --not-after time restriction parameter", func(t *testing.T) {
t.Run("share access with --not-after", func(t *testing.T) {
state := ultest.Setup(commands)
state.Succeed(t, "share", "--not-after", "2022-01-01T15:01:01-01:00", "sj://some/prefix").RequireStdoutGlob(t, `
@ -140,6 +145,26 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : 2022-01-01 16:01:01
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
`)
})
t.Run("share access with --max-object-ttl", func(t *testing.T) {
state := ultest.Setup(commands)
state.Succeed(t, "share", "--max-object-ttl", "720h", "--readonly=false", "sj://some/prefix").RequireStdoutGlob(t, `
Sharing access to satellite *
=========== ACCESS RESTRICTIONS ==========================================================
Download : Allowed
Upload : Allowed
Lists : Allowed
Deletes : Allowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : 720h0m0s
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *
@ -190,6 +215,7 @@ func TestShare(t *testing.T) {
Deletes : Disallowed
NotBefore : No restriction
NotAfter : No restriction
MaxObjectTTL : Not set
Paths : sj://some/prefix
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
Access : *

View File

@ -43,6 +43,16 @@ func (ex *external) OpenProject(ctx context.Context, accessName string, options
UserAgent: uplinkCLIUserAgent,
}
userAgents, err := ex.Dynamic("client.user-agent")
if err != nil {
return nil, err
}
if len(userAgents) > 0 {
if ua := userAgents[len(userAgents)-1]; ua != "" {
config.UserAgent = ua
}
}
if opts.ConnectionPoolOptions != (rpcpool.Options{}) {
if err := transport.SetConnectionPool(ctx, &config, rpcpool.New(opts.ConnectionPoolOptions)); err != nil {
return nil, err

View File

@ -0,0 +1,273 @@
# Node and operator certification
## Abstract
This is a proposal for a small feature and service that allows for nodes and
operators to have signed tags of certain kinds for use in project-specific or
Satellite-specific node selection.
## Background/context
We have a couple of ongoing needs:
* 1099 KYC
* Private storage node networks
* SOC2/HIPAA/etc node certification
* Voting and operator signaling
### 1099 KYC
The United States has a rule that if node operators earn more than $600/year,
we need to file a 1099 for each of them. Our current way of dealing with this
is manual and time consuming, and so it would be nice to automate it.
Ultimately, we should be able to automatically:
1) keep track of which nodes are run by operators under or over the $600
threshold.
2) keep track of if an automated KYC service has signed off that we have the
necessary information to file a 1099.
3) automatically suspend nodes that have earned more than $600 but have not
provided legally required information.
### Private storage node networks
We have seen growing interest from customers that want to bring their own
hard drives, or be extremely choosy about the nodes they are willing to work
with. The current way we are solving this is spinning up private Satellites
that are configured to only work with the nodes those customers provide, but
it would be better if we didn't have to start custom Satellites for this.
Instead, it would be nice to have a per-project configuration on an existing
Satellite that allowed that project to specify a specific subset of verified
or validated nodes, e.g., Project A should be able to say only nodes from
node providers B and C should be selected. Symmetrically, Nodes from providers
B and C may only want to accept data from certain projects, like Project A.
When nodes from providers B and C are added to the Satellite, they should be
able to provide a provider-specific signature, and requirements about
customer-specific requirements, if any.
### SOC2/HIPAA/etc node certification
This is actually just a slightly different shape of the private storage node
network problem, but instead of being provider-specific, it is property
specific.
Perhaps Project D has a compliance requirement. They can only store data
on nodes that meet specific requirements.
Node operators E and F are willing to conform and attest to these compliance
requirements, but don't know about project D. It would be nice if Node
operators E and F could navigate to a compliance portal and see a list of
potential compliance attestations available. For possible compliance
attestations, node operators could sign agreements for these, and then receive
a verified signature that shows their selected compliance options.
Then, Project D's node selection process would filter by nodes that had been
approved for the necessary compliance requirements.
### Voting and operator signaling
As Satellite operators ourselves, we are currently engaged in a discussion about
pricing changes with storage node operators. Future Satellite operators may find
themselves in similar situations. It would be nice if storage node operators
could indicate votes for values. This would potentially be more representative
of network sentiment than posts on a forum.
Note that this isn't a transparent voting scheme, where other voters can see
the votes made, so this may not be a great voting solution in general.
## Design and implementation
I believe there are two basic building blocks that solves all of the above
issues:
* Signed node tags (with potential values)
* A document signing service
### Signed node tags
The network representation:
```
message Tag {
// Note that there is a signal flat namespace of all names per
// signer node id. Signers should be careful to make sure that
// there are no name collisions. For self-signed content-hash
// based values, the name should have the prefix of the content
// hash.
string name = 1;
bytes value = 2; // optional, representation dependent on name.
}
message TagSet {
// must always be set. this is the node the signer is signing for.
bytes node_id = 1;
repeated Tag tags = 2;
// must always be set. this makes sure the signature is signing the
// timestamp inside.
int64 timestamp = 3;
}
message SignedTagSet {
// this is the seralized form of TagSet, serialized so that
// the signature process has something stable to work with.
bytes serialized_tag = 1;
// this is who signed (could be self signed, could be well known).
bytes signer_node_id = 3;
bytes signature = 4;
}
message SignedTagSets {
repeated SignedTagSet tags = 1;
}
```
Note that every tag is signing a name/value pair (value optional) against
a specific node id.
Note also that names are only unique within the namespace of a given signer.
The database representation on the Satellite. N.B.: nothing should be entered
into this database without validation:
```
model signed_tags (
field node_id blob
field name text
field value blob
field timestamp int64
field signer_node_id blob
)
```
The "signer_node_id" is worth more explanation. Every signer should have a
stable node id. Satellites and storage nodes already have one, but any other
service that validates node tags would also need one.
In particular, the document signing service (below) would have its own unique
node id for signing tags, whereas for voting-style tags or tags based on a
content-addressed identifier (e.g. a hash of a document), the nodes would
self-sign.
### Document signing service
We would start a small web service, where users can log in and sign and fill
out documents. This web service would then create a unique activation code
that storage node operators could run on their storage nodes for activation and
signing. They could run `storagenode activate <code>` and then the node would
reach out to the signing service and get a `SignedTag` related to that node
given the information the user provided. The node could then present these
to the satellite.
Ultimately, the document signing service will require a separate design doc,
but here are some considerations for it:
Activation codes must expire shortly. Even Netflix has two hours of validity
for their service code - for a significantly less critical use case. What would
be a usable validity time for our use case? 15 minutes? 1 hour? Should we make
it configurable?
We want to still keep usability in mind for a SNO who needs to activate 500
nodes.
It would be even better if the SNO could force invalidating the activation code
when they are done with it.
As activation codes expire, the SNO should be able to generate a new activation
code if they want to associate a new node to an already signed document.
It should be hard to brute-force activation codes. They shouldn't be simple
numbers (4-digit or 6-digit) but something as complex as UUID.
It's also possible that SNO uses some signature mechanism during signing service
authentication, and the same signature is used for activation. If the same
signature mechanism is used during activation then no token is necessary.
### Update node selection
Once the above two building blocks exist, many problems become much more easily
solvable.
We would want to extend node selection to be able to do queries,
given project-specific configuration, based on these signed_tag values.
Because node selection mostly happens in memory from cached node table data,
it should be easy to add some denormalized data for certain selected cases,
such as:
* Document hashes nodes have self signed.
* Approval states based on well known third party signer nodes (a KYC service).
Once these fields exist, then node selection can happen as before, filtering
for the appropriate value given project settings.
## How these building blocks work for the example use cases
### 1099 KYC
The document signing service would have a KYC (Know Your Customer) form. Once
filled out, the document signing service would make a `TagSet` that includes all
of the answers to the KYC questions, for the given node id, signed by the
document signing service's node id.
The node would hang on to this `SignedTagSet` and submit it along with others
in a `SignedTagSets` to Satellites occasionally (maybe once a month during
node CheckIn).
### Private storage node networks
Storage node provisioning would provide nodes with a signed `SignedTagSet`
from a provisioning service that had its own node id. Then a private Satellite
could be configured to require that all nodes present a `SignedTagSet` signed
by the configured provisioning service that has that node's id in it.
Notably - this functionality could also be solved by the older waitlist node
identity signing certificate process, but we are slowly removing what remains
of that feature over time.
This functionality could also be solved by setting the Satellite's minimum
allowable node id difficulty to the maximum possible difficulty, thus preventing
any automatic node registration, and manually inserting node ids into the
database. This is what we are currently doing for private network trials, but
if `SignedTagSet`s existed, that would be easier.
### SOC2/HIPAA/etc node certification
For any type of document that doesn't require any third party service
(such as government id validation, etc), the document and its fields can be
filled out and self signed by the node, along with a content hash of the
document in question.
The node would create a `TagSet`, where one field is the hash of the legal
document that was agreed upon, and the remaining fields (with names prefixed
by the document's content hash) would be form fields
that the node operator filled in and ascribed to the document. Then, the
`TagSet` would be signed by the node itself. The cryptographic nature of the
content hash inside the `TagSet` would validate what the node operator had
agreed to.
### Voting and operator signaling
Node operators could self sign additional `Tag`s inside of a miscellaneous
`TagSet`, including `Tag`s such as
```
"storage-node-vote-20230611-network-change": "yes"
```
Or similar.
## Open problems
* Revocation? - `TagSets` have a timestamp inside that must be filled out. In
The future, certain tags could have an expiry or updated values or similar.
## Other options
## Wrapup
## Related work

View File

@ -0,0 +1,25 @@
# Mini Cowbell Testplan
&nbsp;
## Background
We want to deploy the entire Storj stack on environments that have kubernetes running on 5 NUCs.
&nbsp;
## Pre-condition
Configuration for satellites that only have 5 node and the recommended RS scheme is [2,3,4,4] where:
- 2 is the number of required pieces to reconstitute the segment.
- 3 is the repair threshold, i.e. if a segment remains with only 3 healthy pieces, it will be repaired.
- 4 is the success threshold, i.e. the number of pieces required for a successful upload or repair.
- 4 is the number of total erasure-coded pieces that will be generated.
| Test Scenario | Test Case | Description | Comments |
|---------------|--------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Upload | Upload with all nodes online | Every file is uploaded to 4 nodes with 2x expansion factor. So one node has no files. | Happy path scenario |
| | Upload with one node offline | If one of five nodes fails and goes offline, 80% of the stored data will lose one erasure-coded piece. The health status of these segments will be reduced from 4 pieces to 3 pieces and will mark these segments for repair. overlay.node.online-window: 4h0m0s -> for about 4 hours the node will still be selected for uploads) | Uploads will continue uninterrupted if the client uses the new refactored upload path. This improved upload logic will request the satellite for a new node if the satellite selects the offline node for the upload, unaware it is already offline. If the client uses the old upload logic, uploads may fail if the satellite selects the offline node (20% chance). When the satellite detects the offline node, all uploads will be successful. |
| Download | Download with one node offline | If one of five nodes fails and goes offline, 80% of the stored data will lose one erasure-coded piece. The health status of these segments will be reduced from 4 pieces to 3 pieces and will mark these segments for repair. overlay.node.online-window: 4h0m0s -> for about 4 hours the node will still be selected for downloads) | |
| Repair | Repair with 2 nodes disqualified | Disqualify 2 nodes so the repair download are still possible but there is no node available for an upload, shouldn't consume download bandwidth and error out early. Only spend download bandwidth when there is at least one node available for an upload | If two nodes go offline, there are remaining pieces in the worst case, which cannot be repaired and is a de facto data loss if the offline nodes are damaged. |
| Audit | | Audits can't identify corrupted pieces with just the minimum number of pieces. Reputation should not increase. Audits should be able to identify corrupted pieces with minumum + 1 pieces. Reputation should decrease. | |
| Upgrades | Nodes restart for upgrades | No more than a single node goes offline for maintenance. Otherwise, normal operation of the network cannot be ensured. | Occasionally, nodes may need to restart due to software updates. This brings the node offline for some period of time |

View File

@ -0,0 +1,58 @@
## Storj Private Cloud - Test Plan
## Test Scenarios
Some test ideas:
- Upload and download some data
- Server side copy and server side move
- Multipart uploads
- Versioning (replace and existing file)
- Audit identifies a bad node and Repair finds new good nodes for the pieces (integration test inclusing audit reservoier sampling, audit job, reverifier, repair checker, repair worker)
- Repair checker and repair worker performance with a million segments in the repair queue (repair queue needs to be ordered by null values first)
- ranged loop performance (do we get better performance from running 2 range loops vs a single range?)
- Upload, Download, List, Delete performance with a million segments in the DB.
- Garbage collection especially the bloom filter creation. Needs to be run from a backup DB and can't be run from the live DB.
- Storage nodes and customer accounting
- Account upload and download limits (redis cache)
- Customer signup with onboarding including creating an access grant
- Token payments
- Graceful exit
- Node selection with geofencing, suspended nodes, disqualified nodes, offline nodes, nodes running outdated versions, nodes out of disk space
Bonus section (technically out of scope but still interresting questions for other tickets)
- Should a private satellite require a stripe account for the billing section? How does the UI look like without a stripe account? How can the customer upgrade to a pro account without having to add a credit card.
- Does the satellite need to be able to send out emails? For signup we have a simulation mode but for other features like project member invite we can't skip the email currently. (Other features with similar issues: storage node notifications, account freeze, password reset)
- What is the plan for the initial vetting period? A brand new satellite with brand new nodes will not be able to upload any date because not enough vetted nodes. -> config change to upload to unvetted nodes. -> risk about uploading too much data to unvetted nodes by keeping this setting longer than nessesary)
&nbsp;
&nbsp;
## [Test Plan Table]
| Test Scenario | Test Case | Description | Comments |
|-----------------------------|------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
| Upload | Small file | Do the upload for 1 KiB, 5 KiB, 1 MiB, 64 MiB files. | |
| | Big file | Do the upload 1024Mb files | |
| | Multipart upload | Upload big file to check the multipart upload | |
| Download | Inline segment | User should download inline segment without any errors | |
| | Remote segment | User should download remote segment without any errors | |
| | Copy 10000 Or More Segments | If a user uploads an object with 10000 segments or more and server side copies it from the source object to the destination object, it should be possible | |
| | Copy inline segment | User should copy inline segment without any errors | |
| | Copy remote segment | User should copy remote segment without any errors | |
| Move | Move object | Move object from one bucket to another bucket | |
| Versioning | Replace and existing file | User should be able to update existing file | |
| DB- Table Segment | Expiration Date | If a user uses Server-side copy, then the source object and the destination object must have the same expiration date | Might be redundant test because of segment table removing |
| DB - Table `segment_copies` | Ancestor_stream_id negative | If a segment with `stream_id = S` hasn't been copied, then the `segment_copies` table has no row having `ancestor_stream_id = S` | Might be redundant test because of segment table removing |
| | Ancestor_stream_id positive | If a segment with `stream_id = S` has been copied, then the `segment_copies` table has at least one row having `ancestor_stream_id = S` | Might be redundant test because of segment table removing |
| Repair | Data repair | Upload some data then kill some nodes and disqualify 1 node(should be enough storage nodes to upload repaired segments). Repaired segment should not contain any piece in the killed and DQ nodes. Downloads the data from new nodes and check that it's the same than the uploaded one. | This test should be in the code |
| Token payments | Multiple Transactions | If a user has a pending transaction and then performs another transaction with a higher nonce using the same address, the new transaction has to wait until the previous transaction with the lower nonce is confirmed (standard behavior of geth, nothing to test for us) | |
| | Invoice Generation | When an invoice is generated and "paid", coupons should be used first, followed by storj balance and then lastly credit card | |
| Performance | Repair queue index has to be null value first. | https://storj.slack.com/archives/C01427KSZ1P/p1589815803066100 | |
| Garbage Collection | Garbage Collection | Needs to be run from a backup DB and can't be run from the live DB | |
| Accounting | Customer | Generate the full invoice cycle | |
| | Storage node | Generate the invoice | |
| Account limits | Upload | Verify that limits are working | |
| | Download | Verify that limits are working | |
| Signup | Customer signup | Customer signup with onboarding including creating an access grant | |

45
go.mod
View File

@ -1,6 +1,6 @@
module storj.io/storj
go 1.18
go 1.19
require (
github.com/VividCortex/ewma v1.2.0
@ -22,21 +22,22 @@ require (
github.com/jackc/pgx/v5 v5.3.1
github.com/jtolds/monkit-hw/v2 v2.0.0-20191108235325-141a0da276b3
github.com/jtolio/eventkit v0.0.0-20230607152326-4668f79ff72d
github.com/jtolio/mito v0.0.0-20230523171229-d78ef06bb77b
github.com/jtolio/noiseconn v0.0.0-20230301220541-88105e6c8ac6
github.com/loov/hrtime v1.0.3
github.com/mattn/go-sqlite3 v1.14.12
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1
github.com/oschwald/maxminddb-golang v1.8.0
github.com/oschwald/maxminddb-golang v1.12.0
github.com/pquerna/otp v1.3.0
github.com/redis/go-redis/v9 v9.0.3
github.com/shopspring/decimal v1.2.0
github.com/spacemonkeygo/monkit/v3 v3.0.20-0.20230419135619-fb89f20752cb
github.com/spacemonkeygo/monkit/v3 v3.0.22
github.com/spacemonkeygo/tlshowdy v0.0.0-20160207005338-8fa2cec1d7cd
github.com/spf13/cobra v1.1.3
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.4
github.com/stripe/stripe-go/v72 v72.90.0
github.com/vbauerster/mpb/v8 v8.4.0
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3
@ -46,25 +47,26 @@ require (
github.com/zeebo/errs v1.3.0
github.com/zeebo/errs/v2 v2.0.3
github.com/zeebo/ini v0.0.0-20210514163846-cc8fbd8d9599
github.com/zeebo/structs v1.0.3-0.20230601144555-f2db46069602
github.com/zyedidia/generic v1.2.1
go.etcd.io/bbolt v1.3.5
go.uber.org/zap v1.16.0
golang.org/x/crypto v0.7.0
golang.org/x/crypto v0.12.0
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db
golang.org/x/net v0.9.0
golang.org/x/net v0.10.0
golang.org/x/oauth2 v0.7.0
golang.org/x/sync v0.1.0
golang.org/x/sys v0.7.0
golang.org/x/term v0.7.0
golang.org/x/text v0.9.0
golang.org/x/sync v0.3.0
golang.org/x/sys v0.11.0
golang.org/x/term v0.11.0
golang.org/x/text v0.12.0
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
gopkg.in/segmentio/analytics-go.v3 v3.1.0
gopkg.in/yaml.v3 v3.0.1
storj.io/common v0.0.0-20230602145716-d6ea82d58b3d
storj.io/common v0.0.0-20230920095429-0ce0a575e6f8
storj.io/drpc v0.0.33
storj.io/monkit-jaeger v0.0.0-20220915074555-d100d7589f41
storj.io/private v0.0.0-20230627140631-807a2f00d0e1
storj.io/uplink v1.10.1-0.20230626081029-035890d408c2
storj.io/private v0.0.0-20230912093002-ca2d4ab44679
storj.io/uplink v1.12.1
)
require (
@ -83,7 +85,7 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang-jwt/jwt v3.2.1+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
@ -107,14 +109,12 @@ require (
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
github.com/quic-go/quic-go v0.32.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
github.com/quic-go/quic-go v0.38.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
@ -128,12 +128,11 @@ require (
github.com/zeebo/float16 v0.1.0 // indirect
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54 // indirect
github.com/zeebo/mwc v0.0.4 // indirect
github.com/zeebo/structs v1.0.3-0.20230601144555-f2db46069602 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/tools v0.6.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/tools v0.9.1 // indirect
google.golang.org/api v0.118.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
@ -141,5 +140,5 @@ require (
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
storj.io/picobuf v0.0.1 // indirect
storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c // indirect
)

85
go.sum
View File

@ -143,12 +143,14 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-oauth2/oauth2/v4 v4.4.2 h1:tWQlR5I4/qhWiyOME67BAFmo622yi+2mm7DMm8DpMdg=
github.com/go-oauth2/oauth2/v4 v4.4.2/go.mod h1:K4DemYzNwwYnIDOPdHtX/7SlO0AHdtlphsTgE7lA3PA=
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
@ -324,6 +326,8 @@ github.com/jtolds/tracetagger/v2 v2.0.0-rc5 h1:SriMFVtftPsQmG+0xaABotz9HnoKoo1QM
github.com/jtolds/tracetagger/v2 v2.0.0-rc5/go.mod h1:61Fh+XhbBONy+RsqkA+xTtmaFbEVL040m9FAF/hTrjQ=
github.com/jtolio/eventkit v0.0.0-20230607152326-4668f79ff72d h1:MAGZUXA8MLSA5oJT1Gua3nLSyTYF2uvBgM4Sfs5+jts=
github.com/jtolio/eventkit v0.0.0-20230607152326-4668f79ff72d/go.mod h1:PXFUrknJu7TkBNyL8t7XWDPtDFFLFrNQQAdsXv9YfJE=
github.com/jtolio/mito v0.0.0-20230523171229-d78ef06bb77b h1:HKvXTXZTeUHXRibg2ilZlkGSQP6A3cs0zXrBd4xMi6M=
github.com/jtolio/mito v0.0.0-20230523171229-d78ef06bb77b/go.mod h1:Mrym6OnPMkBKvN8/uXSkyhFSh6ndKKYE+Q4kxCfQ4V0=
github.com/jtolio/noiseconn v0.0.0-20230301220541-88105e6c8ac6 h1:iVMQyk78uOpX/UKjEbzyBdptXgEz6jwGwo7kM9IQ+3U=
github.com/jtolio/noiseconn v0.0.0-20230301220541-88105e6c8ac6/go.mod h1:MEkhEPFwP3yudWO0lj6vfYpLIB+3eIcuIW+e0AZzUQk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@ -420,15 +424,15 @@ github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk=
github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis=
github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0=
@ -456,14 +460,10 @@ github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.0 h1:T45lASr5q/TrVwt+jrVccmqHhPL2XuSyoCLVCpfOSLc=
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
github.com/redis/go-redis/v9 v9.0.3 h1:+7mmR26M0IvyLxGZUHxu4GiBkJkVDid0Un+j4ScYu4k=
github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -526,8 +526,8 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod
github.com/spacemonkeygo/monkit/v3 v3.0.0-20191108235033-eacca33b3037/go.mod h1:JcK1pCbReQsOsMKF/POFSZCq7drXFybgGmbc27tuwes=
github.com/spacemonkeygo/monkit/v3 v3.0.4/go.mod h1:JcK1pCbReQsOsMKF/POFSZCq7drXFybgGmbc27tuwes=
github.com/spacemonkeygo/monkit/v3 v3.0.18/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4=
github.com/spacemonkeygo/monkit/v3 v3.0.20-0.20230419135619-fb89f20752cb h1:kWLHxcYDcloMFEJMngxuKh8wcLl9RjjeAN2a9AtTtCg=
github.com/spacemonkeygo/monkit/v3 v3.0.20-0.20230419135619-fb89f20752cb/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4=
github.com/spacemonkeygo/monkit/v3 v3.0.22 h1:4/g8IVItBDKLdVnqrdHZrCVPpIrwDBzl1jrV0IHQHDU=
github.com/spacemonkeygo/monkit/v3 v3.0.22/go.mod h1:XkZYGzknZwkD0AKUnZaSXhRiVTLCkq7CWVa3IsE72gA=
github.com/spacemonkeygo/monotime v0.0.0-20180824235756-e3f48a95f98a/go.mod h1:ul4bvvnCOPZgq8w0nTkSmWVg/hauVpFS97Am1YM1XXo=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
@ -565,8 +565,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v72 v72.90.0 h1:fvJ/aL1rHHWRj5buuayb/2ufJued1UR1HEVavsoZoFs=
github.com/stripe/stripe-go/v72 v72.90.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
@ -705,8 +705,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -736,8 +736,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -773,8 +773,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220526153639-5463443f8c37/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -794,8 +794,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -821,7 +821,6 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -841,13 +840,13 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -856,8 +855,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -896,8 +895,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -1013,16 +1012,16 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
storj.io/common v0.0.0-20220719163320-cd2ef8e1b9b0/go.mod h1:mCYV6Ud5+cdbuaxdPD5Zht/HYaIn0sffnnws9ErkrMQ=
storj.io/common v0.0.0-20230602145716-d6ea82d58b3d h1:AXdJxmg4Jqdz1nmogSrImKOHAU+bn8JCy8lHYnTwP0Y=
storj.io/common v0.0.0-20230602145716-d6ea82d58b3d/go.mod h1:zu2L8WdpvfIBrCbBTgPsz4qhHSArYSiDgRcV1RLlIF8=
storj.io/common v0.0.0-20230920095429-0ce0a575e6f8 h1:i+bWPhVnNL6z/TLW3vDZytB6/0bsvJM0a1GhLCxrlxQ=
storj.io/common v0.0.0-20230920095429-0ce0a575e6f8/go.mod h1:ZmeGPzRb2sm705Nwt/WwuH3e6mliShfvvoUNy1bb9v4=
storj.io/drpc v0.0.32/go.mod h1:6rcOyR/QQkSTX/9L5ZGtlZaE2PtXTTZl8d+ulSeeYEg=
storj.io/drpc v0.0.33 h1:yCGZ26r66ZdMP0IcTYsj7WDAUIIjzXk6DJhbhvt9FHI=
storj.io/drpc v0.0.33/go.mod h1:vR804UNzhBa49NOJ6HeLjd2H3MakC1j5Gv8bsOQT6N4=
storj.io/monkit-jaeger v0.0.0-20220915074555-d100d7589f41 h1:SVuEocEhZfFc13J1AmlVLitdGXTVrvmbzN4Z9C9Ms40=
storj.io/monkit-jaeger v0.0.0-20220915074555-d100d7589f41/go.mod h1:iK+dmHZZXQlW7ahKdNSOo+raMk5BDL2wbD62FIeXLWs=
storj.io/picobuf v0.0.1 h1:ekEvxSQCbEjTVIi/qxj2za13SJyfRE37yE30IBkZeT0=
storj.io/picobuf v0.0.1/go.mod h1:7ZTAMs6VesgTHbbhFU79oQ9hDaJ+MD4uoFQZ1P4SEz0=
storj.io/private v0.0.0-20230627140631-807a2f00d0e1 h1:O2+Xjq8H4TKad2cnhvjitK3BtwkGtJ2TfRCHOIN8e7w=
storj.io/private v0.0.0-20230627140631-807a2f00d0e1/go.mod h1:mfdHEaAcTARpd4/Hc6N5uxwB1ZG3jtPdVlle57xzQxQ=
storj.io/uplink v1.10.1-0.20230626081029-035890d408c2 h1:XnJR9egrqvAqx5oCRu2b13ubK0iu0qTX12EAa6lAPhg=
storj.io/uplink v1.10.1-0.20230626081029-035890d408c2/go.mod h1:cDlpDWGJykXfYE7NtO1EeArGFy12K5Xj8pV8ufpUCKE=
storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c h1:or/DtG5uaZpzimL61ahlgAA+MTYn/U3txz4fe+XBFUg=
storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c/go.mod h1:JCuc3C0gzCJHQ4J6SOx/Yjg+QTpX0D+Fvs5H46FETCk=
storj.io/private v0.0.0-20230912093002-ca2d4ab44679 h1:58rShZRrm14tDqc71bnyoFZDvdNIcJ7iBwQWEQZl60U=
storj.io/private v0.0.0-20230912093002-ca2d4ab44679/go.mod h1:6+MGr4KUXEBIOsOstFz1efPkA+8wVVfzsO8RpuAhhB4=
storj.io/uplink v1.12.1 h1:bDc2dI6Q7EXcvPJLZuH9jIOTIf2oKxvW3xKEA+Y5EI0=
storj.io/uplink v1.12.1/go.mod h1:1+czctHG25pMzcUp4Mds6QnoJ7LvbgYA5d1qlpFFexg=

View File

@ -128,7 +128,6 @@ storj.io/storj/satellite/repair/repairer."repair_too_many_nodes_failed" Meter
storj.io/storj/satellite/repair/repairer."repair_unnecessary" Meter
storj.io/storj/satellite/repair/repairer."repairer_segments_below_min_req" Counter
storj.io/storj/satellite/repair/repairer."segment_deleted_before_repair" Meter
storj.io/storj/satellite/repair/repairer."segment_repair_count" IntVal
storj.io/storj/satellite/repair/repairer."segment_time_until_repair" IntVal
storj.io/storj/satellite/repair/repairer."time_for_repair" FloatVal
storj.io/storj/satellite/repair/repairer."time_since_checker_queue" FloatVal

View File

@ -202,10 +202,6 @@ func (obj *DB) Open(ctx context.Context) (*Tx, error) {
}, nil
}
func (obj *DB) NewRx() *Rx {
return &Rx{db: obj}
}
func DeleteAll(ctx context.Context, db *DB) (int64, error) {
tx, err := db.Open(ctx)
if err != nil {
@ -1365,132 +1361,6 @@ func (obj *sqlite3Impl) deleteAll(ctx context.Context) (count int64, err error)
}
type Rx struct {
db *DB
tx *Tx
}
func (rx *Rx) UnsafeTx(ctx context.Context) (unsafe_tx tagsql.Tx, err error) {
tx, err := rx.getTx(ctx)
if err != nil {
return nil, err
}
return tx.Tx, nil
}
func (rx *Rx) getTx(ctx context.Context) (tx *Tx, err error) {
if rx.tx == nil {
if rx.tx, err = rx.db.Open(ctx); err != nil {
return nil, err
}
}
return rx.tx, nil
}
func (rx *Rx) Rebind(s string) string {
return rx.db.Rebind(s)
}
func (rx *Rx) Commit() (err error) {
if rx.tx != nil {
err = rx.tx.Commit()
rx.tx = nil
}
return err
}
func (rx *Rx) Rollback() (err error) {
if rx.tx != nil {
err = rx.tx.Rollback()
rx.tx = nil
}
return err
}
func (rx *Rx) All_Node(ctx context.Context) (
rows []*Node, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.All_Node(ctx)
}
func (rx *Rx) Count_Node(ctx context.Context) (
count int64, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Count_Node(ctx)
}
func (rx *Rx) Create_Node(ctx context.Context,
node_id Node_Id_Field,
node_name Node_Name_Field,
node_public_address Node_PublicAddress_Field,
node_api_secret Node_ApiSecret_Field) (
node *Node, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Create_Node(ctx, node_id, node_name, node_public_address, node_api_secret)
}
func (rx *Rx) Delete_Node_By_Id(ctx context.Context,
node_id Node_Id_Field) (
deleted bool, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Delete_Node_By_Id(ctx, node_id)
}
func (rx *Rx) Get_Node_By_Id(ctx context.Context,
node_id Node_Id_Field) (
node *Node, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Get_Node_By_Id(ctx, node_id)
}
func (rx *Rx) Limited_Node(ctx context.Context,
limit int, offset int64) (
rows []*Node, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Limited_Node(ctx, limit, offset)
}
func (rx *Rx) UpdateNoReturn_Node_By_Id(ctx context.Context,
node_id Node_Id_Field,
update Node_Update_Fields) (
err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.UpdateNoReturn_Node_By_Id(ctx, node_id, update)
}
func (rx *Rx) Update_Node_By_Id(ctx context.Context,
node_id Node_Id_Field,
update Node_Update_Fields) (
node *Node, err error) {
var tx *Tx
if tx, err = rx.getTx(ctx); err != nil {
return
}
return tx.Update_Node_By_Id(ctx, node_id, update)
}
type Methods interface {
All_Node(ctx context.Context) (
rows []*Node, err error)

View File

@ -69,7 +69,9 @@ type DiskSpace struct {
Allocated int64 `json:"allocated"`
Used int64 `json:"usedPieces"`
Trash int64 `json:"usedTrash"`
// Free is the actual amount of free space on the whole disk, not just allocated disk space, in bytes.
Free int64 `json:"free"`
// Available is the amount of free space on the allocated disk space, in bytes.
Available int64 `json:"available"`
Overused int64 `json:"overused"`
}

View File

@ -5,23 +5,62 @@ package apigen
import (
"fmt"
"path"
"reflect"
"regexp"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"storj.io/storj/private/api"
)
var (
groupNameRegExp = regexp.MustCompile(`^([A-Z0-9]\w*)?$`)
groupPrefixRegExp = regexp.MustCompile(`^\w*$`)
)
// API represents specific API's configuration.
type API struct {
// Version is the corresponding version of the API.
// It's concatenated to the BasePath, so assuming the base path is "/api" and the version is "v1"
// the API paths will begin with `/api/v1`.
// When empty, the version doesn't appear in the API paths. If it starts or ends with one or more
// "/", they are stripped from the API endpoint paths.
Version string
Description string
// The package name to use for the Go generated code.
PackageName string
// BasePath is the base path for the API endpoints. E.g. "/api".
// It doesn't require to begin with "/". When empty, "/" is used.
BasePath string
Auth api.Auth
EndpointGroups []*EndpointGroup
}
// Group adds new endpoints group to API.
// name must be `^([A-Z0-9]\w*)?$“
// prefix must be `^\w*$`.
func (a *API) Group(name, prefix string) *EndpointGroup {
if !groupNameRegExp.MatchString(name) {
panic(
fmt.Sprintf(
"invalid name for API Endpoint Group. name must fulfill the regular expression `^([A-Z0-9]\\w*)?$``, got %q",
name,
),
)
}
if !groupPrefixRegExp.MatchString(prefix) {
panic(
fmt.Sprintf(
"invalid prefix for API Endpoint Group %q. prefix must fulfill the regular expression `^\\w*$`, got %q",
name,
prefix,
),
)
}
group := &EndpointGroup{
Name: name,
Prefix: prefix,
@ -32,6 +71,14 @@ func (a *API) Group(name, prefix string) *EndpointGroup {
return group
}
func (a *API) endpointBasePath() string {
if strings.HasPrefix(a.BasePath, "/") {
return path.Join(a.BasePath, a.Version)
}
return "/" + path.Join(a.BasePath, a.Version)
}
// StringBuilder is an extension of strings.Builder that allows for writing formatted lines.
type StringBuilder struct{ strings.Builder }
@ -41,6 +88,17 @@ func (s *StringBuilder) Writelnf(format string, a ...interface{}) {
s.WriteString(fmt.Sprintf(format+"\n", a...))
}
// typeCustomName is a reflect.Type with a customized type's name.
type typeCustomName struct {
reflect.Type
name string
}
func (t typeCustomName) Name() string {
return t.name
}
// getElementaryType simplifies a Go type.
func getElementaryType(t reflect.Type) reflect.Type {
switch t.Kind() {
@ -70,3 +128,15 @@ func isNillableType(t reflect.Type) bool {
}
return false
}
// compoundTypeName create a name composed with base and parts, by joining base as it's and
// capitalizing each part.
func compoundTypeName(base string, parts ...string) string {
caser := cases.Title(language.Und)
titled := make([]string, len(parts))
for i := 0; i < len(parts); i++ {
titled[i] = caser.String(parts[i])
}
return base + strings.Join(titled, "")
}

View File

@ -0,0 +1,47 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package apigen
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAPI_endpointBasePath(t *testing.T) {
cases := []struct {
version string
basePath string
expected string
}{
{version: "", basePath: "", expected: "/"},
{version: "v1", basePath: "", expected: "/v1"},
{version: "v0", basePath: "/", expected: "/v0"},
{version: "", basePath: "api", expected: "/api"},
{version: "v2", basePath: "api", expected: "/api/v2"},
{version: "v2", basePath: "/api", expected: "/api/v2"},
{version: "v2", basePath: "api/", expected: "/api/v2"},
{version: "v2", basePath: "/api/", expected: "/api/v2"},
{version: "/v3", basePath: "api", expected: "/api/v3"},
{version: "/v3/", basePath: "api", expected: "/api/v3"},
{version: "v3/", basePath: "api", expected: "/api/v3"},
{version: "//v3/", basePath: "api", expected: "/api/v3"},
{version: "v3///", basePath: "api", expected: "/api/v3"},
{version: "/v3///", basePath: "/api/test/", expected: "/api/test/v3"},
{version: "/v4.2", basePath: "api/test", expected: "/api/test/v4.2"},
{version: "/v4/2", basePath: "/api/test", expected: "/api/test/v4/2"},
}
for _, c := range cases {
t.Run(fmt.Sprintf("version:%s basePath: %s", c.version, c.basePath), func(t *testing.T) {
a := API{
Version: c.version,
BasePath: c.basePath,
}
assert.Equal(t, c.expected, a.endpointBasePath())
})
}
}

View File

@ -7,6 +7,7 @@ import (
"fmt"
"os"
"reflect"
"regexp"
"strings"
"time"
@ -37,11 +38,27 @@ func (api *API) generateDocumentation() string {
wf("**Description:** %s\n\n", api.Description)
wf("**Version:** `%s`\n\n", api.Version)
wf("<h2 id='list-of-endpoints'>List of Endpoints</h2>\n\n")
getEndpointLink := func(group, endpoint string) string {
fullName := group + "-" + endpoint
fullName = strings.ReplaceAll(fullName, " ", "-")
var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9-]+`)
fullName = nonAlphanumericRegex.ReplaceAllString(fullName, "")
return strings.ToLower(fullName)
}
for _, group := range api.EndpointGroups {
wf("* %s\n", group.Name)
for _, endpoint := range group.endpoints {
wf(" * [%s](#%s)\n", endpoint.Name, getEndpointLink(group.Name, endpoint.Name))
}
}
wf("\n")
for _, group := range api.EndpointGroups {
for _, endpoint := range group.endpoints {
wf("## %s\n\n", endpoint.Name)
wf("<h3 id='%s'>%s (<a href='#list-of-endpoints'>go to full list</a>)</h3>\n\n", getEndpointLink(group.Name, endpoint.Name), endpoint.Name)
wf("%s\n\n", endpoint.Description)
wf("`%s /%s%s`\n\n", endpoint.Method, group.Prefix, endpoint.Path)
wf("`%s %s/%s%s`\n\n", endpoint.Method, api.endpointBasePath(), group.Prefix, endpoint.Path)
if len(endpoint.QueryParams) > 0 {
wf("**Query Params:**\n\n")
@ -66,13 +83,13 @@ func (api *API) generateDocumentation() string {
requestType := reflect.TypeOf(endpoint.Request)
if requestType != nil {
wf("**Request body:**\n\n")
wf("```json\n%s\n```\n\n", getTypeNameRecursively(requestType, 0))
wf("```typescript\n%s\n```\n\n", getTypeNameRecursively(requestType, 0))
}
responseType := reflect.TypeOf(endpoint.Response)
if responseType != nil {
wf("**Response body:**\n\n")
wf("```json\n%s\n```\n\n", getTypeNameRecursively(responseType, 0))
wf("```typescript\n%s\n```\n\n", getTypeNameRecursively(responseType, 0))
}
}
}
@ -123,7 +140,6 @@ func getTypeNameRecursively(t reflect.Type, level int) string {
elemType := t.Elem()
if elemType.Kind() == reflect.Uint8 { // treat []byte as string in docs
return prefix + "string"
}
return fmt.Sprintf("%s[\n%s\n%s]\n", prefix, getTypeNameRecursively(elemType, level+1), prefix)
case reflect.Struct:
@ -132,7 +148,7 @@ func getTypeNameRecursively(t reflect.Type, level int) string {
if typeName != "unknown" {
toReturn := typeName
if len(elaboration) > 0 {
toReturn += " (" + elaboration + ")"
toReturn += " // " + elaboration
}
return toReturn
}
@ -150,7 +166,7 @@ func getTypeNameRecursively(t reflect.Type, level int) string {
typeName, elaboration := getDocType(t)
toReturn := typeName
if len(elaboration) > 0 {
toReturn += " (" + elaboration + ")"
toReturn += " // " + elaboration
}
return toReturn
}

View File

@ -4,21 +4,39 @@
package apigen
import (
"fmt"
"net/http"
"reflect"
"strings"
)
// Endpoint represents endpoint's configuration.
type Endpoint struct {
// Name is a free text used to name the endpoint for documentation purpose.
// It cannot be empty.
Name string
// Description is a free text to describe the endpoint for documentation purpose.
Description string
// MethodName is the name of method of the service interface which handles the business logic of
// this endpoint.
// It must fulfill the Go language specification for method names
// (https://go.dev/ref/spec#MethodName)
// TODO: Should we rename this field to be something like ServiceMethodName?
MethodName string
// RequestName is the name of the method used to name the method in the client side code. When not
// set, MethodName is used.
// TODO: Should we delete this field in favor of always using MethodName?
RequestName string
NoCookieAuth bool
NoAPIAuth bool
// Request is the type that defines the format of the request body.
Request interface{}
// Response is the type that defines the format of the response body.
Response interface{}
// QueryParams is the list of query parameters that the endpoint accepts.
QueryParams []Param
// PathParams is the list of path parameters that appear in the path associated with this
// endpoint.
PathParams []Param
}
@ -39,7 +57,34 @@ type fullEndpoint struct {
Method string
}
// requestType guarantees to return a named Go type associated to the Endpoint.Request field.
func (fe fullEndpoint) requestType() reflect.Type {
t := reflect.TypeOf(fe.Request)
if t.Name() == "" {
name := fe.RequestName
if name == "" {
name = fe.MethodName
}
t = typeCustomName{Type: t, name: compoundTypeName(name, "Request")}
}
return t
}
// responseType guarantees to return a named Go type associated to the Endpoint.Response field.
func (fe fullEndpoint) responseType() reflect.Type {
t := reflect.TypeOf(fe.Response)
if t.Name() == "" {
t = typeCustomName{Type: t, name: compoundTypeName(fe.MethodName, "Response")}
}
return t
}
// EndpointGroup represents endpoints group.
// You should always create a group using API.Group because it validates the field values to
// guarantee correct code generation.
type EndpointGroup struct {
Name string
Prefix string
@ -47,27 +92,43 @@ type EndpointGroup struct {
}
// Get adds new GET endpoint to endpoints group.
// It panics if path doesn't begin with '/'.
func (eg *EndpointGroup) Get(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodGet, endpoint)
}
// Patch adds new PATCH endpoint to endpoints group.
// It panics if path doesn't begin with '/'.
func (eg *EndpointGroup) Patch(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodPatch, endpoint)
}
// Post adds new POST endpoint to endpoints group.
// It panics if path doesn't begin with '/'.
func (eg *EndpointGroup) Post(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodPost, endpoint)
}
// Delete adds new DELETE endpoint to endpoints group.
// It panics if path doesn't begin with '/'.
func (eg *EndpointGroup) Delete(path string, endpoint *Endpoint) {
eg.addEndpoint(path, http.MethodDelete, endpoint)
}
// addEndpoint adds new endpoint to endpoints list.
// It panics if path doesn't begin with '/'.
func (eg *EndpointGroup) addEndpoint(path, method string, endpoint *Endpoint) {
if !strings.HasPrefix(path, "/") {
panic(
fmt.Sprintf(
"invalid path for method %q of EndpointGroup %q. path must start with slash, got %q",
method,
eg.Name,
path,
),
)
}
ep := &fullEndpoint{*endpoint, path, method}
for i, e := range eg.endpoints {
if e.Path == path && e.Method == method {
@ -91,3 +152,16 @@ func NewParam(name string, instance interface{}) Param {
Type: reflect.TypeOf(instance),
}
}
// namedType guarantees to return a named Go type. where defines where the param is defined (e.g.
// path, query, etc.).
func (p Param) namedType(ep Endpoint, where string) reflect.Type {
if p.Type.Name() == "" {
return typeCustomName{
Type: p.Type,
name: compoundTypeName(ep.MethodName, where, "param", p.Name),
}
}
return p.Type
}

View File

@ -16,44 +16,81 @@ import (
"storj.io/common/uuid"
"storj.io/storj/private/api"
"storj.io/storj/private/apigen/example/myapi"
)
const dateLayout = "2006-01-02T15:04:05.999Z"
var ErrTestapiAPI = errs.Class("example testapi api")
var ErrDocsAPI = errs.Class("example docs api")
type TestAPIService interface {
GenTestAPI(ctx context.Context, path string, id uuid.UUID, date time.Time, request struct{ Content string }) (*struct {
ID uuid.UUID
Date time.Time
PathParam string
Body string
type DocumentsService interface {
GetOne(ctx context.Context, path string) (*myapi.Document, api.HTTPError)
UpdateContent(ctx context.Context, path string, id uuid.UUID, date time.Time, request struct {
Content string "json:\"content\""
}) (*struct {
ID uuid.UUID "json:\"id\""
Date time.Time "json:\"date\""
PathParam string "json:\"pathParam\""
Body string "json:\"body\""
}, api.HTTPError)
}
// TestAPIHandler is an api handler that exposes all testapi related functionality.
type TestAPIHandler struct {
// DocumentsHandler is an api handler that exposes all docs related functionality.
type DocumentsHandler struct {
log *zap.Logger
mon *monkit.Scope
service TestAPIService
service DocumentsService
auth api.Auth
}
func NewTestAPI(log *zap.Logger, mon *monkit.Scope, service TestAPIService, router *mux.Router, auth api.Auth) *TestAPIHandler {
handler := &TestAPIHandler{
func NewDocuments(log *zap.Logger, mon *monkit.Scope, service DocumentsService, router *mux.Router, auth api.Auth) *DocumentsHandler {
handler := &DocumentsHandler{
log: log,
mon: mon,
service: service,
auth: auth,
}
testapiRouter := router.PathPrefix("/api/v0/testapi").Subrouter()
testapiRouter.HandleFunc("/{path}", handler.handleGenTestAPI).Methods("POST")
docsRouter := router.PathPrefix("/api/v0/docs").Subrouter()
docsRouter.HandleFunc("/{path}", handler.handleGetOne).Methods("GET")
docsRouter.HandleFunc("/{path}", handler.handleUpdateContent).Methods("POST")
return handler
}
func (h *TestAPIHandler) handleGenTestAPI(w http.ResponseWriter, r *http.Request) {
func (h *DocumentsHandler) handleGetOne(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer h.mon.Task()(&ctx)(&err)
w.Header().Set("Content-Type", "application/json")
path, ok := mux.Vars(r)["path"]
if !ok {
api.ServeError(h.log, w, http.StatusBadRequest, errs.New("missing path route param"))
return
}
ctx, err = h.auth.IsAuthenticated(ctx, r, true, true)
if err != nil {
h.auth.RemoveAuthCookie(w)
api.ServeError(h.log, w, http.StatusUnauthorized, err)
return
}
retVal, httpErr := h.service.GetOne(ctx, path)
if httpErr.Err != nil {
api.ServeError(h.log, w, httpErr.Status, httpErr.Err)
return
}
err = json.NewEncoder(w).Encode(retVal)
if err != nil {
h.log.Debug("failed to write json GetOne response", zap.Error(ErrDocsAPI.Wrap(err)))
}
}
func (h *DocumentsHandler) handleUpdateContent(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer h.mon.Task()(&ctx)(&err)
@ -90,7 +127,9 @@ func (h *TestAPIHandler) handleGenTestAPI(w http.ResponseWriter, r *http.Request
return
}
payload := struct{ Content string }{}
payload := struct {
Content string "json:\"content\""
}{}
if err = json.NewDecoder(r.Body).Decode(&payload); err != nil {
api.ServeError(h.log, w, http.StatusBadRequest, err)
return
@ -103,7 +142,7 @@ func (h *TestAPIHandler) handleGenTestAPI(w http.ResponseWriter, r *http.Request
return
}
retVal, httpErr := h.service.GenTestAPI(ctx, path, id, date, payload)
retVal, httpErr := h.service.UpdateContent(ctx, path, id, date, payload)
if httpErr.Err != nil {
api.ServeError(h.log, w, httpErr.Status, httpErr.Err)
return
@ -111,6 +150,6 @@ func (h *TestAPIHandler) handleGenTestAPI(w http.ResponseWriter, r *http.Request
err = json.NewEncoder(w).Encode(retVal)
if err != nil {
h.log.Debug("failed to write json GenTestAPI response", zap.Error(ErrTestapiAPI.Wrap(err)))
h.log.Debug("failed to write json UpdateContent response", zap.Error(ErrDocsAPI.Wrap(err)))
}
}

View File

@ -0,0 +1,77 @@
# API Docs
**Description:**
**Version:** `v0`
<h2 id='list-of-endpoints'>List of Endpoints</h2>
* Documents
* [Get One](#documents-get-one)
* [Update Content](#documents-update-content)
<h3 id='documents-get-one'>Get One (<a href='#list-of-endpoints'>go to full list</a>)</h3>
Get one document with the specified version
`GET /api/v0/docs/{path}`
**Path Params:**
| name | type | elaboration |
|---|---|---|
| `path` | `string` | |
**Response body:**
```typescript
{
id: string // UUID formatted as `00000000-0000-0000-0000-000000000000`
date: string // Date timestamp formatted as `2006-01-02T15:00:00Z`
pathParam: string
body: string
version: number
}
```
<h3 id='documents-update-content'>Update Content (<a href='#list-of-endpoints'>go to full list</a>)</h3>
Update the content of the document with the specified path and ID if the last update is before the indicated date
`POST /api/v0/docs/{path}`
**Query Params:**
| name | type | elaboration |
|---|---|---|
| `id` | `string` | UUID formatted as `00000000-0000-0000-0000-000000000000` |
| `date` | `string` | Date timestamp formatted as `2006-01-02T15:00:00Z` |
**Path Params:**
| name | type | elaboration |
|---|---|---|
| `path` | `string` | |
**Request body:**
```typescript
{
content: string
}
```
**Response body:**
```typescript
{
id: string // UUID formatted as `00000000-0000-0000-0000-000000000000`
date: string // Date timestamp formatted as `2006-01-02T15:00:00Z`
pathParam: string
body: string
}
```

View File

@ -0,0 +1,52 @@
// AUTOGENERATED BY private/apigen
// DO NOT EDIT.
import { HttpClient } from '@/utils/httpClient';
import { Time, UUID } from '@/types/common';
export class Document {
id: UUID;
date: Time;
pathParam: string;
body: string;
version: number;
}
export class UpdateContentRequest {
content: string;
}
export class UpdateContentResponse {
id: UUID;
date: Time;
pathParam: string;
body: string;
}
export class docsHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/docs';
public async GetOne(path: string): Promise<Document> {
const fullPath = `${this.ROOT_PATH}/${path}`;
const response = await this.http.get(fullPath);
if (response.ok) {
return response.json().then((body) => body as Document);
}
const err = await response.json();
throw new Error(err.error);
}
public async UpdateContent(request: UpdateContentRequest, path: string, id: UUID, date: Time): Promise<UpdateContentResponse> {
const u = new URL(`${this.ROOT_PATH}/${path}`);
u.searchParams.set('id', id);
u.searchParams.set('date', date);
const fullPath = u.toString();
const response = await this.http.post(fullPath, JSON.stringify(request));
if (response.ok) {
return response.json().then((body) => body as UpdateContentResponse);
}
const err = await response.json();
throw new Error(err.error);
}
}

View File

@ -11,22 +11,37 @@ import (
"storj.io/common/uuid"
"storj.io/storj/private/apigen"
"storj.io/storj/private/apigen/example/myapi"
)
func main() {
a := &apigen.API{PackageName: "example"}
a := &apigen.API{PackageName: "example", Version: "v0", BasePath: "/api"}
g := a.Group("TestAPI", "testapi")
g := a.Group("Documents", "docs")
g.Get("/{path}", &apigen.Endpoint{
Name: "Get One",
Description: "Get one document with the specified version",
MethodName: "GetOne",
Response: myapi.Document{},
PathParams: []apigen.Param{
apigen.NewParam("path", ""),
},
})
g.Post("/{path}", &apigen.Endpoint{
MethodName: "GenTestAPI",
Name: "Update Content",
Description: "Update the content of the document with the specified path and ID if the last update is before the indicated date",
MethodName: "UpdateContent",
Response: struct {
ID uuid.UUID
Date time.Time
PathParam string
Body string
ID uuid.UUID `json:"id"`
Date time.Time `json:"date"`
PathParam string `json:"pathParam"`
Body string `json:"body"`
}{},
Request: struct {
Content string `json:"content"`
}{},
Request: struct{ Content string }{},
QueryParams: []apigen.Param{
apigen.NewParam("id", uuid.UUID{}),
apigen.NewParam("date", time.Time{}),
@ -37,4 +52,6 @@ func main() {
})
a.MustWriteGo("api.gen.go")
a.MustWriteTS("client-api.gen.ts")
a.MustWriteDocs("apidocs.gen.md")
}

View File

@ -0,0 +1,19 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package myapi
import (
"time"
"storj.io/common/uuid"
)
// Document is a retrieved document.
type Document struct {
ID uuid.UUID `json:"id"`
Date time.Time `json:"date"`
PathParam string `json:"pathParam"`
Body string `json:"body"`
Version uint `json:"version"`
}

View File

@ -22,10 +22,11 @@ import (
const DateFormat = "2006-01-02T15:04:05.999Z"
// MustWriteGo writes generated Go code into a file.
// If an error occurs, it panics.
func (a *API) MustWriteGo(path string) {
generated, err := a.generateGo()
if err != nil {
panic(errs.Wrap(err))
panic(err)
}
err = os.WriteFile(path, generated, 0644)
@ -41,7 +42,12 @@ func (a *API) generateGo() ([]byte, error) {
getPackageName := func(path string) string {
pathPackages := strings.Split(path, "/")
return pathPackages[len(pathPackages)-1]
name := pathPackages[len(pathPackages)-1]
if name == "main" {
panic(errs.New(`invalid package name. Your types cannot be defined in a package named "main"`))
}
return name
}
imports := struct {
@ -100,7 +106,12 @@ func (a *API) generateGo() ([]byte, error) {
for _, group := range a.EndpointGroups {
i("github.com/zeebo/errs")
pf("var Err%sAPI = errs.Class(\"%s %s api\")", cases.Title(language.Und).String(group.Prefix), a.PackageName, group.Prefix)
pf(
"var Err%sAPI = errs.Class(\"%s %s api\")",
cases.Title(language.Und).String(group.Prefix),
a.PackageName,
group.Prefix,
)
}
pf("")
@ -167,10 +178,16 @@ func (a *API) generateGo() ([]byte, error) {
pf("auth: auth,")
pf("}")
pf("")
pf("%sRouter := router.PathPrefix(\"/api/v0/%s\").Subrouter()", group.Prefix, group.Prefix)
pf("%sRouter := router.PathPrefix(\"%s/%s\").Subrouter()", group.Prefix, a.endpointBasePath(), group.Prefix)
for _, endpoint := range group.endpoints {
handlerName := "handle" + endpoint.MethodName
pf("%sRouter.HandleFunc(\"%s\", handler.%s).Methods(\"%s\")", group.Prefix, endpoint.Path, handlerName, endpoint.Method)
pf(
"%sRouter.HandleFunc(\"%s\", handler.%s).Methods(\"%s\")",
group.Prefix,
endpoint.Path,
handlerName,
endpoint.Method,
)
}
pf("")
pf("return handler")
@ -242,7 +259,11 @@ func (a *API) generateGo() ([]byte, error) {
pf("")
pf("err = json.NewEncoder(w).Encode(retVal)")
pf("if err != nil {")
pf("h.log.Debug(\"failed to write json %s response\", zap.Error(Err%sAPI.Wrap(err)))", endpoint.MethodName, cases.Title(language.Und).String(group.Prefix))
pf(
"h.log.Debug(\"failed to write json %s response\", zap.Error(Err%sAPI.Wrap(err)))",
endpoint.MethodName,
cases.Title(language.Und).String(group.Prefix),
)
pf("}")
pf("}")
}
@ -282,7 +303,7 @@ func (a *API) generateGo() ([]byte, error) {
output, err := format.Source([]byte(result.String()))
if err != nil {
return nil, err
return nil, errs.Wrap(err)
}
return output, nil

View File

@ -25,16 +25,17 @@ import (
"storj.io/storj/private/api"
"storj.io/storj/private/apigen"
"storj.io/storj/private/apigen/example"
"storj.io/storj/private/apigen/example/myapi"
)
type (
auth struct{}
service struct{}
response = struct {
ID uuid.UUID
Date time.Time
PathParam string
Body string
ID uuid.UUID `json:"id"`
Date time.Time `json:"date"`
PathParam string `json:"pathParam"`
Body string `json:"body"`
}
)
@ -44,7 +45,22 @@ func (a auth) IsAuthenticated(ctx context.Context, r *http.Request, isCookieAuth
func (a auth) RemoveAuthCookie(w http.ResponseWriter) {}
func (s service) GenTestAPI(ctx context.Context, pathParam string, id uuid.UUID, date time.Time, body struct{ Content string }) (*response, api.HTTPError) {
func (s service) GetOne(
ctx context.Context,
pathParam string,
) (*myapi.Document, api.HTTPError) {
return &myapi.Document{}, api.HTTPError{}
}
func (s service) UpdateContent(
ctx context.Context,
pathParam string,
id uuid.UUID,
date time.Time,
body struct {
Content string `json:"content"`
},
) (*response, api.HTTPError) {
return &response{
ID: id,
Date: date,
@ -90,7 +106,7 @@ func TestAPIServer(t *testing.T) {
defer ctx.Cleanup()
router := mux.NewRouter()
example.NewTestAPI(zaptest.NewLogger(t), monkit.Package(), service{}, router, auth{})
example.NewDocuments(zaptest.NewLogger(t), monkit.Package(), service{}, router, auth{})
server := httptest.NewServer(router)
defer server.Close()
@ -106,7 +122,7 @@ func TestAPIServer(t *testing.T) {
}
resp, err := send(ctx, http.MethodPost,
fmt.Sprintf("%s/api/v0/testapi/%s?id=%s&date=%s",
fmt.Sprintf("%s/api/v0/docs/%s?id=%s&date=%s",
server.URL,
expected.PathParam,
url.QueryEscape(expected.ID.String()),
@ -118,10 +134,11 @@ func TestAPIServer(t *testing.T) {
var actual map[string]string
require.NoError(t, json.Unmarshal(resp, &actual))
for _, key := range []string{"ID", "Date", "PathParam", "Body"} {
for _, key := range []string{"id", "date", "pathParam", "body"} {
require.Contains(t, actual, key)
}
require.Equal(t, expected.ID.String(), actual["ID"])
require.Equal(t, expected.Date.Format(apigen.DateFormat), actual["Date"])
require.Equal(t, expected.Body, actual["Body"])
require.Equal(t, expected.ID.String(), actual["id"])
require.Equal(t, expected.Date.Format(apigen.DateFormat), actual["date"])
require.Equal(t, expected.PathParam, actual["pathParam"])
require.Equal(t, expected.Body, actual["body"])
}

View File

@ -13,6 +13,7 @@ import (
)
// MustWriteTS writes generated TypeScript code into a file.
// If an error occurs, it panics.
func (a *API) MustWriteTS(path string) {
f := newTSGenFile(path, a)
@ -64,17 +65,19 @@ func (f *tsGenFile) generateTS() {
}
func (f *tsGenFile) registerTypes() {
// TODO: what happen with path parameters?
for _, group := range f.api.EndpointGroups {
for _, method := range group.endpoints {
if method.Request != nil {
f.types.Register(reflect.TypeOf(method.Request))
f.types.Register(method.requestType())
}
if method.Response != nil {
f.types.Register(reflect.TypeOf(method.Response))
f.types.Register(method.responseType())
}
if len(method.QueryParams) > 0 {
for _, p := range method.QueryParams {
t := getElementaryType(p.Type)
// TODO: Is this call needed? this breaks the named type for slices and arrays and pointers.
t := getElementaryType(p.namedType(method.Endpoint, "query"))
f.types.Register(t)
}
}
@ -85,7 +88,7 @@ func (f *tsGenFile) registerTypes() {
func (f *tsGenFile) createAPIClient(group *EndpointGroup) {
f.pf("\nexport class %sHttpApi%s {", group.Prefix, strings.ToUpper(f.api.Version))
f.pf("\tprivate readonly http: HttpClient = new HttpClient();")
f.pf("\tprivate readonly ROOT_PATH: string = '/api/%s/%s';", f.api.Version, group.Prefix)
f.pf("\tprivate readonly ROOT_PATH: string = '%s/%s';", f.api.endpointBasePath(), group.Prefix)
for _, method := range group.endpoints {
f.pf("")
@ -94,21 +97,36 @@ func (f *tsGenFile) createAPIClient(group *EndpointGroup) {
returnStmt := "return"
returnType := "void"
if method.Response != nil {
returnType = TypescriptTypeName(getElementaryType(reflect.TypeOf(method.Response)))
if v := reflect.ValueOf(method.Response); v.Kind() == reflect.Array || v.Kind() == reflect.Slice {
respType := method.responseType()
returnType = TypescriptTypeName(getElementaryType(respType))
// TODO: see if this is needed after we are creating types for array and slices
if respType.Kind() == reflect.Array || respType.Kind() == reflect.Slice {
returnType = fmt.Sprintf("Array<%s>", returnType)
}
returnStmt += fmt.Sprintf(" response.json().then((body) => body as %s)", returnType)
}
returnStmt += ";"
f.pf("\tpublic async %s(%s): Promise<%s> {", method.RequestName, funcArgs, returnType)
f.pf("\t\tconst path = `%s`;", path)
methodName := method.RequestName
if methodName == "" {
methodName = method.MethodName
}
f.pf("\tpublic async %s(%s): Promise<%s> {", methodName, funcArgs, returnType)
if len(method.QueryParams) > 0 {
f.pf("\t\tconst u = new URL(`%s`);", path)
for _, p := range method.QueryParams {
f.pf("\t\tu.searchParams.set('%s', %s);", p.Name, p.Name)
}
f.pf("\t\tconst fullPath = u.toString();")
} else {
f.pf("\t\tconst fullPath = `%s`;", path)
}
if method.Request != nil {
f.pf("\t\tconst response = await this.http.%s(path, JSON.stringify(request));", strings.ToLower(method.Method))
f.pf("\t\tconst response = await this.http.%s(fullPath, JSON.stringify(request));", strings.ToLower(method.Method))
} else {
f.pf("\t\tconst response = await this.http.%s(path);", strings.ToLower(method.Method))
f.pf("\t\tconst response = await this.http.%s(fullPath);", strings.ToLower(method.Method))
}
f.pf("\t\tif (response.ok) {")
@ -131,24 +149,18 @@ func (f *tsGenFile) getArgsAndPath(method *fullEndpoint) (funcArgs, path string)
path = "${this.ROOT_PATH}" + path
if method.Request != nil {
t := getElementaryType(reflect.TypeOf(method.Request))
// TODO: This should map slices and arrays because a request could be one of them.
t := getElementaryType(method.requestType())
funcArgs += fmt.Sprintf("request: %s, ", TypescriptTypeName(t))
}
for _, p := range method.PathParams {
funcArgs += fmt.Sprintf("%s: %s, ", p.Name, TypescriptTypeName(p.Type))
funcArgs += fmt.Sprintf("%s: %s, ", p.Name, TypescriptTypeName(p.namedType(method.Endpoint, "path")))
path += fmt.Sprintf("/${%s}", p.Name)
}
for i, p := range method.QueryParams {
if i == 0 {
path += "?"
} else {
path += "&"
}
funcArgs += fmt.Sprintf("%s: %s, ", p.Name, TypescriptTypeName(p.Type))
path += fmt.Sprintf("%s=${%s}", p.Name, p.Name)
for _, p := range method.QueryParams {
funcArgs += fmt.Sprintf("%s: %s, ", p.Name, TypescriptTypeName(p.namedType(method.Endpoint, "query")))
}
path = strings.ReplaceAll(path, "//", "/")

View File

@ -36,46 +36,76 @@ type Types struct {
// Register registers a type for generation.
func (types *Types) Register(t reflect.Type) {
if t.Name() == "" {
panic("register an anonymous type is not supported. All the types must have a name")
}
types.top[t] = struct{}{}
}
// All returns a slice containing every top-level type and their dependencies.
//
// TODO: see how to have a better implementation for adding to seen, uniqueNames, and all.
func (types *Types) All() []reflect.Type {
seen := map[reflect.Type]struct{}{}
uniqueNames := map[string]struct{}{}
all := []reflect.Type{}
var walk func(t reflect.Type)
walk = func(t reflect.Type) {
var walk func(t reflect.Type, alternateTypeName string)
walk = func(t reflect.Type, altTypeName string) {
if _, ok := seen[t]; ok {
return
}
seen[t] = struct{}{}
all = append(all, t)
// Type isn't seen it but it has the same name than a seen it one.
// This cannot be because we would generate more than one TypeScript type with the same name.
if _, ok := uniqueNames[t.Name()]; ok {
panic(fmt.Sprintf("Found different types with the same name (%s)", t.Name()))
}
if _, ok := commonClasses[t]; ok {
seen[t] = struct{}{}
uniqueNames[t.Name()] = struct{}{}
all = append(all, t)
return
}
switch t.Kind() {
switch k := t.Kind(); k {
// TODO: Does reflect.Ptr to be registered?, I believe that could skip it and only register
// the type that points to.
case reflect.Array, reflect.Ptr, reflect.Slice:
walk(t.Elem())
t = typeCustomName{Type: t, name: compoundTypeName(altTypeName, k.String())}
seen[t] = struct{}{}
uniqueNames[t.Name()] = struct{}{}
all = append(all, t)
walk(t.Elem(), altTypeName)
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
walk(t.Field(i).Type)
if t.Name() == "" {
t = typeCustomName{Type: t, name: altTypeName}
}
case reflect.Bool:
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
case reflect.Float32, reflect.Float64:
case reflect.String:
break
seen[t] = struct{}{}
uniqueNames[t.Name()] = struct{}{}
all = append(all, t)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
walk(field.Type, compoundTypeName(altTypeName, field.Name))
}
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64,
reflect.String:
seen[t] = struct{}{}
uniqueNames[t.Name()] = struct{}{}
all = append(all, t)
default:
panic(fmt.Sprintf("type '%s' is not supported", t.Kind().String()))
}
}
for t := range types.top {
walk(t)
walk(t, t.Name())
}
sort.Slice(all, func(i, j int) bool {
@ -96,6 +126,8 @@ func (types *Types) GenerateTypescriptDefinitions() string {
if _, ok := commonClasses[t]; ok {
return false
}
// TODO, we should be able to handle arrays and slices as defined types now
return t.Kind() == reflect.Struct
})
@ -154,6 +186,7 @@ func (types *Types) getTypescriptImports() string {
}
// TypescriptTypeName gets the corresponding TypeScript type for a provided reflect.Type.
// If the type is an anonymous struct, it returns an empty string.
func TypescriptTypeName(t reflect.Type) string {
if override, ok := commonClasses[t]; ok {
return override

View File

@ -0,0 +1,129 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package apigen
import (
"reflect"
"testing"
"github.com/stretchr/testify/require"
)
type testTypesValoration struct {
Points uint
}
func TestTypes(t *testing.T) {
t.Run("Register panics with anonymous types", func(t *testing.T) {
types := NewTypes()
require.Panics(t, func() {
types.Register(reflect.TypeOf([2]int{}))
}, "array")
require.Panics(t, func() {
types.Register(reflect.TypeOf([]float64{}))
}, "slice")
require.Panics(t, func() {
types.Register(reflect.TypeOf(struct{}{}))
}, "struct")
})
t.Run("All returns nested types", func(t *testing.T) {
typesList := []reflect.Type{
reflect.TypeOf(true),
reflect.TypeOf(int64(10)),
reflect.TypeOf(uint8(9)),
reflect.TypeOf(float64(99.9)),
reflect.TypeOf("this is a test"),
reflect.TypeOf(testTypesValoration{}),
}
types := NewTypes()
for _, li := range typesList {
types.Register(li)
}
allTypes := types.All()
require.Len(t, allTypes, 7, "total number of types")
require.Subset(t, allTypes, typesList, "all types contains at least the registered ones")
})
t.Run("All nested structs and slices", func(t *testing.T) {
types := NewTypes()
types.Register(
typeCustomName{
Type: reflect.TypeOf(struct {
Name string
Addresses []struct {
Address string
PO string
}
Job struct {
Company string
Position string
StartingYear uint
}
Documents []struct {
Path string
Content string
Valoration testTypesValoration
}
}{}),
name: "Response",
})
allTypes := types.All()
require.Len(t, allTypes, 9, "total number of types")
typesNames := []string{}
for _, tp := range allTypes {
typesNames = append(typesNames, tp.Name())
}
require.ElementsMatch(t, []string{
"string", "uint",
"Response",
"ResponseAddressesSlice", "ResponseAddresses",
"ResponseJob",
"ResponseDocumentsSlice", "ResponseDocuments", "testTypesValoration",
}, typesNames)
})
t.Run("All panic types without unique names", func(t *testing.T) {
types := NewTypes()
types.Register(typeCustomName{
Type: reflect.TypeOf(struct {
Name string
Addresses []struct {
Address string
PO string
}
Job struct {
Company string
Position string
StartingYear uint
}
Documents []struct {
Path string
Content string
Valoration testTypesValoration
}
}{}),
name: "Response",
})
types.Register(typeCustomName{
Type: reflect.TypeOf(struct {
Reference string
}{}),
name: "Response",
})
require.Panics(t, func() {
types.All()
})
})
}

View File

@ -27,7 +27,9 @@ message DiskSpaceResponse {
int64 allocated = 1;
int64 used_pieces = 2;
int64 used_trash = 3;
// Free is the actual amount of free space on the whole disk, not just allocated disk space, in bytes.
int64 free = 4;
// Available is the amount of free space on the allocated disk space, in bytes.
int64 available = 5;
int64 overused = 6;
}

View File

@ -55,6 +55,7 @@ func (sender *SMTPSender) communicate(ctx context.Context, client *smtp.Client,
// before creating SMTPSender
host, _, _ := net.SplitHostPort(sender.ServerAddress)
if sender.Auth != nil {
// send smtp hello or ehlo msg and establish connection over tls
err := client.StartTLS(&tls.Config{ServerName: host})
if err != nil {
@ -65,8 +66,9 @@ func (sender *SMTPSender) communicate(ctx context.Context, client *smtp.Client,
if err != nil {
return err
}
}
err = client.Mail(sender.From.Address)
err := client.Mail(sender.From.Address)
if err != nil {
return err
}

View File

@ -0,0 +1,51 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package server
import (
"os/exec"
"strconv"
"strings"
"sync"
"syscall"
"go.uber.org/zap"
)
const tcpFastOpen = 1025
func setTCPFastOpen(fd uintptr, _queue int) error {
return syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, tcpFastOpen, 1)
}
var tryInitFastOpenOnce sync.Once
var initFastOpenPossiblyEnabled bool
// tryInitFastOpen returns true if fastopen support is possibly enabled.
func tryInitFastOpen(log *zap.Logger) bool {
tryInitFastOpenOnce.Do(func() {
initFastOpenPossiblyEnabled = true
output, err := exec.Command("sysctl", "-n", "net.inet.tcp.fastopen.server_enable").Output()
if err != nil {
log.Sugar().Infof("kernel support for tcp fast open unknown")
initFastOpenPossiblyEnabled = true
return
}
enabled, err := strconv.ParseBool(strings.TrimSpace(string(output)))
if err != nil {
log.Sugar().Infof("kernel support for tcp fast open unparsable")
initFastOpenPossiblyEnabled = true
return
}
if enabled {
log.Sugar().Infof("kernel support for server-side tcp fast open enabled.")
} else {
log.Sugar().Infof("kernel support for server-side tcp fast open not enabled.")
log.Sugar().Infof("enable with: sysctl net.inet.tcp.fastopen.server_enable=1")
log.Sugar().Infof("enable on-boot by setting net.inet.tcp.fastopen.server_enable=1 in /etc/sysctl.conf")
}
initFastOpenPossiblyEnabled = enabled
})
return initFastOpenPossiblyEnabled
}

View File

@ -1,8 +1,8 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
//go:build !linux && !windows
// +build !linux,!windows
//go:build !linux && !windows && !freebsd
// +build !linux,!windows,!freebsd
package server

View File

@ -4,22 +4,44 @@
package server
import (
"context"
"net"
"sync"
"syscall"
"go.uber.org/zap"
)
const tcpFastOpenServer = 15
const tcpFastOpen = 15 // Corresponds to TCP_FASTOPEN from MS SDK
func setTCPFastOpen(fd uintptr, queue int) error {
return syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_TCP, tcpFastOpenServer, 1)
return syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_TCP, tcpFastOpen, 1)
}
var tryInitFastOpenOnce sync.Once
var initFastOpenPossiblyEnabled bool
// tryInitFastOpen returns true if fastopen support is possibly enabled.
func tryInitFastOpen(*zap.Logger) bool {
// should we log or check something along the lines of
// netsh int tcp set global fastopen=enabled
// netsh int tcp set global fastopenfallback=disabled
// ?
return false
tryInitFastOpenOnce.Do(func() {
// TCP-FASTOPEN is supported as of Windows 10 build 1607, but is
// enabled per socket. If the socket option isn't supported then the
// call to opt-in will fail. So as long as we can set up a listening
// socket with the right socket option set, we should be good.
if listener, err := (&net.ListenConfig{
Control: func(network, addr string, c syscall.RawConn) error {
var sockOptErr error
if controlErr := c.Control(func(fd uintptr) {
sockOptErr = setTCPFastOpen(fd, 0) // queue is unused
}); controlErr != nil {
return controlErr
}
return sockOptErr
},
}).Listen(context.Background(), "tcp", "127.0.0.1:0"); err == nil {
listener.Close()
initFastOpenPossiblyEnabled = true
}
})
return initFastOpenPossiblyEnabled
}

View File

@ -95,6 +95,8 @@ func TestHybridConnector_Basic(t *testing.T) {
}
func TestHybridConnector_QUICOnly(t *testing.T) {
t.Skip("QUIC is currently broken")
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1,
StorageNodeCount: 0,

View File

@ -66,10 +66,10 @@ type Satellite struct {
Core *satellite.Core
API *satellite.API
UI *satellite.UI
Repairer *satellite.Repairer
Auditor *satellite.Auditor
Admin *satellite.Admin
GC *satellite.GarbageCollection
GCBF *satellite.GarbageCollectionBF
RangedLoop *satellite.RangedLoop
@ -173,12 +173,17 @@ type Satellite struct {
Service *mailservice.Service
}
Console struct {
ConsoleBackend struct {
Listener net.Listener
Service *console.Service
Endpoint *consoleweb.Server
}
ConsoleFrontend struct {
Listener net.Listener
Endpoint *consoleweb.Server
}
NodeStats struct {
Endpoint *nodestats.Endpoint
}
@ -256,7 +261,7 @@ func (system *Satellite) AddProject(ctx context.Context, ownerID uuid.UUID, name
if err != nil {
return nil, errs.Wrap(err)
}
project, err := system.API.Console.Service.CreateProject(ctx, console.ProjectInfo{
project, err := system.API.Console.Service.CreateProject(ctx, console.UpsertProjectInfo{
Name: name,
})
if err != nil {
@ -285,7 +290,6 @@ func (system *Satellite) Close() error {
system.Repairer.Close(),
system.Auditor.Close(),
system.Admin.Close(),
system.GC.Close(),
system.GCBF.Close(),
)
}
@ -300,6 +304,11 @@ func (system *Satellite) Run(ctx context.Context) (err error) {
group.Go(func() error {
return errs2.IgnoreCanceled(system.API.Run(ctx))
})
if system.UI != nil {
group.Go(func() error {
return errs2.IgnoreCanceled(system.UI.Run(ctx))
})
}
group.Go(func() error {
return errs2.IgnoreCanceled(system.Repairer.Run(ctx))
})
@ -309,9 +318,6 @@ func (system *Satellite) Run(ctx context.Context) (err error) {
group.Go(func() error {
return errs2.IgnoreCanceled(system.Admin.Run(ctx))
})
group.Go(func() error {
return errs2.IgnoreCanceled(system.GC.Run(ctx))
})
group.Go(func() error {
return errs2.IgnoreCanceled(system.GCBF.Run(ctx))
})
@ -524,6 +530,15 @@ func (planet *Planet) newSatellite(ctx context.Context, prefix string, index int
return nil, errs.Wrap(err)
}
// only run if front-end endpoints on console back-end server are disabled.
var ui *satellite.UI
if !config.Console.FrontendEnable {
ui, err = planet.newUI(ctx, index, identity, config, api.ExternalAddress, api.Console.Listener.Addr().String())
if err != nil {
return nil, errs.Wrap(err)
}
}
adminPeer, err := planet.newAdmin(ctx, index, identity, db, metabaseDB, config, versionInfo)
if err != nil {
return nil, errs.Wrap(err)
@ -539,11 +554,6 @@ func (planet *Planet) newSatellite(ctx context.Context, prefix string, index int
return nil, errs.Wrap(err)
}
gcPeer, err := planet.newGarbageCollection(ctx, index, identity, db, metabaseDB, config, versionInfo)
if err != nil {
return nil, errs.Wrap(err)
}
gcBFPeer, err := planet.newGarbageCollectionBF(ctx, index, db, metabaseDB, config, versionInfo)
if err != nil {
return nil, errs.Wrap(err)
@ -558,23 +568,23 @@ func (planet *Planet) newSatellite(ctx context.Context, prefix string, index int
peer.Mail.EmailReminders.TestSetLinkAddress("http://" + api.Console.Listener.Addr().String() + "/")
}
return createNewSystem(prefix, log, config, peer, api, repairerPeer, auditorPeer, adminPeer, gcPeer, gcBFPeer, rangedLoopPeer), nil
return createNewSystem(prefix, log, config, peer, api, ui, repairerPeer, auditorPeer, adminPeer, gcBFPeer, rangedLoopPeer), nil
}
// createNewSystem makes a new Satellite System and exposes the same interface from
// before we split out the API. In the short term this will help keep all the tests passing
// without much modification needed. However long term, we probably want to rework this
// so it represents how the satellite will run when it is made up of many processes.
func createNewSystem(name string, log *zap.Logger, config satellite.Config, peer *satellite.Core, api *satellite.API, repairerPeer *satellite.Repairer, auditorPeer *satellite.Auditor, adminPeer *satellite.Admin, gcPeer *satellite.GarbageCollection, gcBFPeer *satellite.GarbageCollectionBF, rangedLoopPeer *satellite.RangedLoop) *Satellite {
func createNewSystem(name string, log *zap.Logger, config satellite.Config, peer *satellite.Core, api *satellite.API, ui *satellite.UI, repairerPeer *satellite.Repairer, auditorPeer *satellite.Auditor, adminPeer *satellite.Admin, gcBFPeer *satellite.GarbageCollectionBF, rangedLoopPeer *satellite.RangedLoop) *Satellite {
system := &Satellite{
Name: name,
Config: config,
Core: peer,
API: api,
UI: ui,
Repairer: repairerPeer,
Auditor: auditorPeer,
Admin: adminPeer,
GC: gcPeer,
GCBF: gcBFPeer,
RangedLoop: rangedLoopPeer,
}
@ -622,7 +632,7 @@ func createNewSystem(name string, log *zap.Logger, config satellite.Config, peer
system.Audit.Reporter = auditorPeer.Audit.Reporter
system.Audit.ContainmentSyncChore = peer.Audit.ContainmentSyncChore
system.GarbageCollection.Sender = gcPeer.GarbageCollection.Sender
system.GarbageCollection.Sender = peer.GarbageCollection.Sender
system.ExpiredDeletion.Chore = peer.ExpiredDeletion.Chore
system.ZombieDeletion.Chore = peer.ZombieDeletion.Chore
@ -666,6 +676,15 @@ func (planet *Planet) newAPI(ctx context.Context, index int, identity *identity.
return satellite.NewAPI(log, identity, db, metabaseDB, revocationDB, liveAccounting, rollupsWriteCache, &config, versionInfo, nil)
}
func (planet *Planet) newUI(ctx context.Context, index int, identity *identity.FullIdentity, config satellite.Config, satelliteAddr, consoleAPIAddr string) (_ *satellite.UI, err error) {
defer mon.Task()(&ctx)(&err)
prefix := "satellite-ui" + strconv.Itoa(index)
log := planet.log.Named(prefix)
return satellite.NewUI(log, identity, &config, nil, satelliteAddr, consoleAPIAddr)
}
func (planet *Planet) newAdmin(ctx context.Context, index int, identity *identity.FullIdentity, db satellite.DB, metabaseDB *metabase.DB, config satellite.Config, versionInfo version.Info) (_ *satellite.Admin, err error) {
defer mon.Task()(&ctx)(&err)
@ -713,20 +732,6 @@ func (cache rollupsWriteCacheCloser) Close() error {
return cache.RollupsWriteCache.CloseAndFlush(context.TODO())
}
func (planet *Planet) newGarbageCollection(ctx context.Context, index int, identity *identity.FullIdentity, db satellite.DB, metabaseDB *metabase.DB, config satellite.Config, versionInfo version.Info) (_ *satellite.GarbageCollection, err error) {
defer mon.Task()(&ctx)(&err)
prefix := "satellite-gc" + strconv.Itoa(index)
log := planet.log.Named(prefix)
revocationDB, err := revocation.OpenDBFromCfg(ctx, config.Server.Config)
if err != nil {
return nil, errs.Wrap(err)
}
planet.databases = append(planet.databases, revocationDB)
return satellite.NewGarbageCollection(log, identity, db, metabaseDB, revocationDB, versionInfo, &config, nil)
}
func (planet *Planet) newGarbageCollectionBF(ctx context.Context, index int, db satellite.DB, metabaseDB *metabase.DB, config satellite.Config, versionInfo version.Info) (_ *satellite.GarbageCollectionBF, err error) {
defer mon.Task()(&ctx)(&err)
@ -746,7 +751,6 @@ func (planet *Planet) newRangedLoop(ctx context.Context, index int, db satellite
prefix := "satellite-ranged-loop" + strconv.Itoa(index)
log := planet.log.Named(prefix)
return satellite.NewRangedLoop(log, db, metabaseDB, &config, nil)
}

View File

@ -21,6 +21,7 @@ import (
"storj.io/common/peertls/tlsopts"
"storj.io/common/storj"
"storj.io/private/debug"
"storj.io/storj/cmd/storagenode/internalcmd"
"storj.io/storj/private/revocation"
"storj.io/storj/private/server"
"storj.io/storj/storagenode"
@ -215,6 +216,10 @@ func (planet *Planet) newStorageNode(ctx context.Context, prefix string, index,
MinDownloadTimeout: 2 * time.Minute,
},
}
// enable the lazy filewalker
config.Pieces.EnableLazyFilewalker = true
if planet.config.Reconfigure.StorageNode != nil {
planet.config.Reconfigure.StorageNode(index, &config)
}
@ -275,6 +280,21 @@ func (planet *Planet) newStorageNode(ctx context.Context, prefix string, index,
return nil, errs.New("error while trying to issue new api key: %v", err)
}
{
// set up the used space lazyfilewalker filewalker
cmd := internalcmd.NewUsedSpaceFilewalkerCmd()
cmd.Logger = log.Named("used-space-filewalker")
cmd.Ctx = ctx
peer.Storage2.LazyFileWalker.TestingSetUsedSpaceCmd(cmd)
}
{
// set up the GC lazyfilewalker filewalker
cmd := internalcmd.NewGCFilewalkerCmd()
cmd.Logger = log.Named("gc-filewalker")
cmd.Ctx = ctx
peer.Storage2.LazyFileWalker.TestingSetGCCmd(cmd)
}
return &StorageNode{
Name: prefix,
Config: config,

View File

@ -27,6 +27,7 @@ import (
"storj.io/storj/private/revocation"
"storj.io/storj/private/server"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite/nodeselection"
"storj.io/uplink"
"storj.io/uplink/private/metaclient"
)
@ -105,9 +106,15 @@ func TestDownloadWithSomeNodesOffline(t *testing.T) {
}
// confirm that we marked the correct number of storage nodes as offline
nodes, err := satellite.Overlay.Service.Reliable(ctx)
allNodes, err := satellite.Overlay.Service.GetParticipatingNodes(ctx)
require.NoError(t, err)
require.Len(t, nodes, len(planet.StorageNodes)-toKill)
online := make([]nodeselection.SelectedNode, 0, len(allNodes))
for _, node := range allNodes {
if node.Online {
online = append(online, node)
}
}
require.Len(t, online, len(planet.StorageNodes)-toKill)
// we should be able to download data without any of the original nodes
newData, err := ul.Download(ctx, satellite, "testbucket", "test/path")

View File

@ -6,16 +6,16 @@ package version
import _ "unsafe" // needed for go:linkname
//go:linkname buildTimestamp storj.io/private/version.buildTimestamp
var buildTimestamp string
var buildTimestamp string = "1696509940"
//go:linkname buildCommitHash storj.io/private/version.buildCommitHash
var buildCommitHash string
var buildCommitHash string = "98e778e26c859329ff016e7ef0ed2aaed40e52d0"
//go:linkname buildVersion storj.io/private/version.buildVersion
var buildVersion string
var buildVersion string = "v1.89.5"
//go:linkname buildRelease storj.io/private/version.buildRelease
var buildRelease string
var buildRelease string = "true"
// ensure that linter understands that the variables are being used.
func init() { use(buildTimestamp, buildCommitHash, buildVersion, buildRelease) }

View File

@ -4,24 +4,32 @@
package web
import (
"context"
"encoding/json"
"net/http"
"go.uber.org/zap"
"storj.io/common/http/requestid"
)
// ServeJSONError writes a JSON error to the response output stream.
func ServeJSONError(log *zap.Logger, w http.ResponseWriter, status int, err error) {
ServeCustomJSONError(log, w, status, err, err.Error())
func ServeJSONError(ctx context.Context, log *zap.Logger, w http.ResponseWriter, status int, err error) {
ServeCustomJSONError(ctx, log, w, status, err, err.Error())
}
// ServeCustomJSONError writes a JSON error with a custom message to the response output stream.
func ServeCustomJSONError(log *zap.Logger, w http.ResponseWriter, status int, err error, msg string) {
func ServeCustomJSONError(ctx context.Context, log *zap.Logger, w http.ResponseWriter, status int, err error, msg string) {
fields := []zap.Field{
zap.Int("code", status),
zap.String("message", msg),
zap.Error(err),
}
if requestID := requestid.FromContext(ctx); requestID != "" {
fields = append(fields, zap.String("requestID", requestID))
}
switch status {
case http.StatusNoContent:
return

View File

@ -87,12 +87,12 @@ func (rl *RateLimiter) Limit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key, err := rl.keyFunc(r)
if err != nil {
ServeCustomJSONError(rl.log, w, http.StatusInternalServerError, err, internalServerErrMsg)
ServeCustomJSONError(r.Context(), rl.log, w, http.StatusInternalServerError, err, internalServerErrMsg)
return
}
limit := rl.getUserLimit(key)
if !limit.Allow() {
ServeJSONError(rl.log, w, http.StatusTooManyRequests, errs.New(rateLimitErrMsg))
ServeJSONError(r.Context(), rl.log, w, http.StatusTooManyRequests, errs.New(rateLimitErrMsg))
return
}
next.ServeHTTP(w, r)

View File

@ -111,16 +111,16 @@ type ProjectUsageByDay struct {
// BucketUsage consist of total bucket usage for period.
type BucketUsage struct {
ProjectID uuid.UUID
BucketName string
ProjectID uuid.UUID `json:"projectID"`
BucketName string `json:"bucketName"`
Storage float64
Egress float64
ObjectCount int64
SegmentCount int64
Storage float64 `json:"storage"`
Egress float64 `json:"egress"`
ObjectCount int64 `json:"objectCount"`
SegmentCount int64 `json:"segmentCount"`
Since time.Time
Before time.Time
Since time.Time `json:"since"`
Before time.Time `json:"before"`
}
// BucketUsageCursor holds info for bucket usage
@ -133,15 +133,15 @@ type BucketUsageCursor struct {
// BucketUsagePage represents bucket usage page result.
type BucketUsagePage struct {
BucketUsages []BucketUsage
BucketUsages []BucketUsage `json:"bucketUsages"`
Search string
Limit uint
Offset uint64
Search string `json:"search"`
Limit uint `json:"limit"`
Offset uint64 `json:"offset"`
PageCount uint
CurrentPage uint
TotalCount uint64
PageCount uint `json:"pageCount"`
CurrentPage uint `json:"currentPage"`
TotalCount uint64 `json:"totalCount"`
}
// BucketUsageRollup is total bucket usage info
@ -219,6 +219,8 @@ type ProjectAccounting interface {
GetProjectSettledBandwidthTotal(ctx context.Context, projectID uuid.UUID, from time.Time) (_ int64, err error)
// GetProjectBandwidth returns project allocated bandwidth for the specified year, month and day.
GetProjectBandwidth(ctx context.Context, projectID uuid.UUID, year int, month time.Month, day int, asOfSystemInterval time.Duration) (int64, error)
// GetProjectSettledBandwidth returns the used settled bandwidth for the specified year and month.
GetProjectSettledBandwidth(ctx context.Context, projectID uuid.UUID, year int, month time.Month, asOfSystemInterval time.Duration) (int64, error)
// GetProjectDailyBandwidth returns bandwidth (allocated and settled) for the specified day.
GetProjectDailyBandwidth(ctx context.Context, projectID uuid.UUID, year int, month time.Month, day int) (int64, int64, int64, error)
// DeleteProjectBandwidthBefore deletes project bandwidth rollups before the given time

View File

@ -26,6 +26,7 @@ type Config struct {
StorageBackend string `help:"what to use for storing real-time accounting data"`
BandwidthCacheTTL time.Duration `default:"5m" help:"bandwidth cache key time to live"`
AsOfSystemInterval time.Duration `default:"-10s" help:"as of system interval"`
BatchSize int `default:"5000" help:"how much projects usage should be requested from redis cache at once"`
}
// OpenCache creates a new accounting.Cache instance using the type specified backend in
@ -49,7 +50,7 @@ func OpenCache(ctx context.Context, log *zap.Logger, config Config) (accounting.
backendType = parts[0]
switch backendType {
case "redis":
return openRedisLiveAccounting(ctx, config.StorageBackend)
return openRedisLiveAccounting(ctx, config.StorageBackend, config.BatchSize)
default:
return nil, Error.New("unrecognized live accounting backend specifier %q. Currently only redis is supported", backendType)
}

View File

@ -6,6 +6,7 @@ package live_test
import (
"context"
"math/rand"
"strconv"
"testing"
"time"
@ -136,22 +137,31 @@ func TestGetAllProjectTotals(t *testing.T) {
require.NoError(t, err)
}
usage, err := cache.GetAllProjectTotals(ctx)
for _, batchSize := range []int{1, 2, 3, 10, 13, 10000} {
t.Run("batch-size-"+strconv.Itoa(batchSize), func(t *testing.T) {
config.BatchSize = batchSize
testCache, err := live.OpenCache(ctx, zaptest.NewLogger(t).Named("live-accounting"), config)
require.NoError(t, err)
defer ctx.Check(testCache.Close)
usage, err := testCache.GetAllProjectTotals(ctx)
require.NoError(t, err)
require.Len(t, usage, len(projectIDs))
// make sure each project ID and total was received
for _, projID := range projectIDs {
totalStorage, err := cache.GetProjectStorageUsage(ctx, projID)
totalStorage, err := testCache.GetProjectStorageUsage(ctx, projID)
require.NoError(t, err)
assert.Equal(t, totalStorage, usage[projID].Storage)
totalSegments, err := cache.GetProjectSegmentUsage(ctx, projID)
totalSegments, err := testCache.GetProjectSegmentUsage(ctx, projID)
require.NoError(t, err)
assert.Equal(t, totalSegments, usage[projID].Segments)
}
})
}
})
}
}
func TestLiveAccountingCache_ProjectBandwidthUsage_expiration(t *testing.T) {

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/redis/go-redis/v9"
"github.com/zeebo/errs/v2"
"storj.io/common/uuid"
"storj.io/storj/satellite/accounting"
@ -18,6 +19,8 @@ import (
type redisLiveAccounting struct {
client *redis.Client
batchSize int
}
// openRedisLiveAccounting returns a redisLiveAccounting cache instance.
@ -29,7 +32,7 @@ type redisLiveAccounting struct {
// it fails then it returns an instance and accounting.ErrSystemOrNetError
// because it means that Redis may not be operative at this precise moment but
// it may be in future method calls as it handles automatically reconnects.
func openRedisLiveAccounting(ctx context.Context, address string) (*redisLiveAccounting, error) {
func openRedisLiveAccounting(ctx context.Context, address string, batchSize int) (*redisLiveAccounting, error) {
opts, err := redis.ParseURL(address)
if err != nil {
return nil, accounting.ErrInvalidArgument.Wrap(err)
@ -37,6 +40,7 @@ func openRedisLiveAccounting(ctx context.Context, address string) (*redisLiveAcc
cache := &redisLiveAccounting{
client: redis.NewClient(opts),
batchSize: batchSize,
}
// ping here to verify we are able to connect to Redis with the initialized client.
@ -52,7 +56,7 @@ func openRedisLiveAccounting(ctx context.Context, address string) (*redisLiveAcc
func (cache *redisLiveAccounting) GetProjectStorageUsage(ctx context.Context, projectID uuid.UUID) (totalUsed int64, err error) {
defer mon.Task()(&ctx, projectID)(&err)
return cache.getInt64(ctx, string(projectID[:]))
return cache.getInt64(ctx, createStorageProjectIDKey(projectID))
}
// GetProjectBandwidthUsage returns the current bandwidth usage
@ -175,7 +179,7 @@ func (cache *redisLiveAccounting) AddProjectSegmentUsageUpToLimit(ctx context.Co
func (cache *redisLiveAccounting) AddProjectStorageUsage(ctx context.Context, projectID uuid.UUID, spaceUsed int64) (err error) {
defer mon.Task()(&ctx, projectID, spaceUsed)(&err)
_, err = cache.client.IncrBy(ctx, string(projectID[:]), spaceUsed).Result()
_, err = cache.client.IncrBy(ctx, createStorageProjectIDKey(projectID), spaceUsed).Result()
if err != nil {
return accounting.ErrSystemOrNetError.New("Redis incrby failed: %w", err)
}
@ -216,6 +220,7 @@ func (cache *redisLiveAccounting) GetAllProjectTotals(ctx context.Context) (_ ma
defer mon.Task()(&ctx)(&err)
projects := make(map[uuid.UUID]accounting.Usage)
it := cache.client.Scan(ctx, 0, "*", 0).Iterator()
for it.Next(ctx) {
key := it.Val()
@ -231,58 +236,112 @@ func (cache *redisLiveAccounting) GetAllProjectTotals(ctx context.Context) (_ ma
return nil, accounting.ErrUnexpectedValue.New("cannot parse the key as UUID; key=%q", key)
}
usage := accounting.Usage{}
if seenUsage, seen := projects[projectID]; seen {
if seenUsage.Segments != 0 {
continue
}
usage = seenUsage
}
segmentUsage, err := cache.GetProjectSegmentUsage(ctx, projectID)
if err != nil {
if accounting.ErrKeyNotFound.Has(err) {
continue
}
return nil, err
}
usage.Segments = segmentUsage
projects[projectID] = usage
projects[projectID] = accounting.Usage{}
} else {
projectID, err := uuid.FromBytes([]byte(key))
if err != nil {
return nil, accounting.ErrUnexpectedValue.New("cannot parse the key as UUID; key=%q", key)
}
usage := accounting.Usage{}
if seenUsage, seen := projects[projectID]; seen {
if seenUsage.Storage != 0 {
continue
projects[projectID] = accounting.Usage{}
}
}
usage = seenUsage
return cache.fillUsage(ctx, projects)
}
storageUsage, err := cache.getInt64(ctx, key)
func (cache *redisLiveAccounting) fillUsage(ctx context.Context, projects map[uuid.UUID]accounting.Usage) (_ map[uuid.UUID]accounting.Usage, err error) {
defer mon.Task()(&ctx)(&err)
if len(projects) == 0 {
return nil, nil
}
projectIDs := make([]uuid.UUID, 0, cache.batchSize)
segmentKeys := make([]string, 0, cache.batchSize)
storageKeys := make([]string, 0, cache.batchSize)
fetchProjectsUsage := func() error {
if len(projectIDs) == 0 {
return nil
}
segmentResult, err := cache.client.MGet(ctx, segmentKeys...).Result()
if err != nil {
if accounting.ErrKeyNotFound.Has(err) {
continue
return accounting.ErrGetProjectLimitCache.Wrap(err)
}
storageResult, err := cache.client.MGet(ctx, storageKeys...).Result()
if err != nil {
return accounting.ErrGetProjectLimitCache.Wrap(err)
}
// Note, because we are using a cache, it might be empty and not contain the
// information we are looking for -- or they might be still empty for some reason.
for i, projectID := range projectIDs {
segmentsUsage, err := parseAnyAsInt64(segmentResult[i])
if err != nil {
return errs.Wrap(err)
}
storageUsage, err := parseAnyAsInt64(storageResult[i])
if err != nil {
return errs.Wrap(err)
}
projects[projectID] = accounting.Usage{
Segments: segmentsUsage,
Storage: storageUsage,
}
}
return nil
}
for projectID := range projects {
projectIDs = append(projectIDs, projectID)
segmentKeys = append(segmentKeys, createSegmentProjectIDKey(projectID))
storageKeys = append(storageKeys, createStorageProjectIDKey(projectID))
if len(projectIDs) >= cache.batchSize {
err := fetchProjectsUsage()
if err != nil {
return nil, err
}
usage.Storage = storageUsage
projects[projectID] = usage
projectIDs = projectIDs[:0]
segmentKeys = segmentKeys[:0]
storageKeys = storageKeys[:0]
}
}
err = fetchProjectsUsage()
if err != nil {
return nil, err
}
return projects, nil
}
func parseAnyAsInt64(v any) (int64, error) {
if v == nil {
return 0, nil
}
s, ok := v.(string)
if !ok {
return 0, accounting.ErrUnexpectedValue.New("cannot parse the value as int64; val=%q", v)
}
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return 0, accounting.ErrUnexpectedValue.New("cannot parse the value as int64; val=%q", v)
}
return i, nil
}
// Close the DB connection.
func (cache *redisLiveAccounting) Close() error {
err := cache.client.Close()
@ -325,3 +384,8 @@ func createBandwidthProjectIDKey(projectID uuid.UUID, now time.Time) string {
func createSegmentProjectIDKey(projectID uuid.UUID) string {
return string(projectID[:]) + ":segment"
}
// createStorageProjectIDKey creates the storage project key.
func createStorageProjectIDKey(projectID uuid.UUID) string {
return string(projectID[:])
}

View File

@ -41,6 +41,7 @@ type ProjectLimitConfig struct {
// each project ID if they differ from the default limits.
type ProjectLimitCache struct {
projectLimitDB ProjectLimitDB
defaultMaxUsage memory.Size
defaultMaxBandwidth memory.Size
defaultMaxSegments int64
@ -121,10 +122,6 @@ func (c *ProjectLimitCache) getProjectLimits(ctx context.Context, projectID uuid
defaultSegments := c.defaultMaxSegments
projectLimits.Segments = &defaultSegments
}
if projectLimits.Segments == nil {
defaultSegments := c.defaultMaxSegments
projectLimits.Segments = &defaultSegments
}
return projectLimits, nil
}

View File

@ -218,6 +218,17 @@ func (usage *Service) GetProjectBandwidthTotals(ctx context.Context, projectID u
return total, ErrProjectUsage.Wrap(err)
}
// GetProjectSettledBandwidth returns total amount of settled bandwidth used for past 30 days.
func (usage *Service) GetProjectSettledBandwidth(ctx context.Context, projectID uuid.UUID) (_ int64, err error) {
defer mon.Task()(&ctx, projectID)(&err)
// from the beginning of the current month
year, month, _ := usage.nowFn().Date()
total, err := usage.projectAccountingDB.GetProjectSettledBandwidth(ctx, projectID, year, month, usage.asOfSystemInterval)
return total, ErrProjectUsage.Wrap(err)
}
// GetProjectSegmentTotals returns total amount of allocated segments used for past 30 days.
func (usage *Service) GetProjectSegmentTotals(ctx context.Context, projectID uuid.UUID) (total int64, err error) {
defer mon.Task()(&ctx, projectID)(&err)
@ -319,3 +330,8 @@ func (usage *Service) AddProjectStorageUsage(ctx context.Context, projectID uuid
func (usage *Service) SetNow(now func() time.Time) {
usage.nowFn = now
}
// TestSetAsOfSystemInterval allows tests to set Service asOfSystemInterval value.
func (usage *Service) TestSetAsOfSystemInterval(asOfSystemInterval time.Duration) {
usage.asOfSystemInterval = asOfSystemInterval
}

View File

@ -182,7 +182,8 @@ func TestProjectSegmentLimit(t *testing.T) {
},
},
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
data := testrand.Bytes(160 * memory.KiB)
// tally self-corrects live accounting, however, it may cause things to be temporarily off by a few segments.
planet.Satellites[0].Accounting.Tally.Loop.Pause()
// set limit manually to 10 segments
accountingDB := planet.Satellites[0].DB.ProjectAccounting()
@ -190,6 +191,7 @@ func TestProjectSegmentLimit(t *testing.T) {
require.NoError(t, err)
// successful upload
data := testrand.Bytes(160 * memory.KiB)
err = planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "test/path/0", data)
require.NoError(t, err)
@ -203,14 +205,17 @@ func TestProjectSegmentLimit(t *testing.T) {
func TestProjectSegmentLimitInline(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, UplinkCount: 1}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
data := testrand.Bytes(1 * memory.KiB)
SatelliteCount: 1, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
// tally self-corrects live accounting, however, it may cause things to be temporarily off by a few segments.
planet.Satellites[0].Accounting.Tally.Loop.Pause()
// set limit manually to 10 segments
accountingDB := planet.Satellites[0].DB.ProjectAccounting()
err := accountingDB.UpdateProjectSegmentLimit(ctx, planet.Uplinks[0].Projects[0].ID, 10)
require.NoError(t, err)
data := testrand.Bytes(1 * memory.KiB)
for i := 0; i < 10; i++ {
// successful upload
err = planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "test/path/"+strconv.Itoa(i), data)
@ -260,14 +265,17 @@ func TestProjectBandwidthLimitWithoutCache(t *testing.T) {
func TestProjectSegmentLimitMultipartUpload(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, UplinkCount: 1}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
data := testrand.Bytes(1 * memory.KiB)
SatelliteCount: 1, UplinkCount: 1,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
// tally self-corrects live accounting, however, it may cause things to be temporarily off by a few segments.
planet.Satellites[0].Accounting.Tally.Loop.Pause()
// set limit manually to 10 segments
accountingDB := planet.Satellites[0].DB.ProjectAccounting()
err := accountingDB.UpdateProjectSegmentLimit(ctx, planet.Uplinks[0].Projects[0].ID, 4)
require.NoError(t, err)
data := testrand.Bytes(1 * memory.KiB)
for i := 0; i < 4; i++ {
// successful upload
err = planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "test/path/"+strconv.Itoa(i), data)

View File

@ -174,11 +174,32 @@ Disables the user's mfa.
#### PUT /api/users/{user-email}/freeze
Freezes a user account so no uploads or downloads may occur.
This is a billing freeze the user can exit automatically by paying their invoice.
#### DELETE /api/users/{user-email}/freeze
Unfreezes a user account so uploads and downloads may resume.
#### DELETE /api/users/{user-email}/warning
Removes the warning status from a user's account.
#### PATCH /api/users/{user-email}/geofence
Sets the account level geofence for the user.
Example request:
```json
{
"region": "US"
}
```
#### DELETE /api/users/{user-email}/geofence
Removes the account level geofence for the user.
### OAuth Client Management
Manages oauth clients known to the Satellite.

View File

@ -29,10 +29,15 @@ func (server *Server) addAPIKey(w http.ResponseWriter, r *http.Request) {
return
}
projectUUID, err := uuid.FromString(projectUUIDString)
project, err := server.getProjectByAnyID(ctx, projectUUIDString)
if errors.Is(err, sql.ErrNoRows) {
sendJSONError(w, "project with specified uuid does not exist",
"", http.StatusNotFound)
return
}
if err != nil {
sendJSONError(w, "invalid project-uuid",
err.Error(), http.StatusBadRequest)
sendJSONError(w, "error getting project",
err.Error(), http.StatusInternalServerError)
return
}
@ -60,7 +65,7 @@ func (server *Server) addAPIKey(w http.ResponseWriter, r *http.Request) {
return
}
_, err = server.db.Console().APIKeys().GetByNameAndProjectID(ctx, input.Name, projectUUID)
_, err = server.db.Console().APIKeys().GetByNameAndProjectID(ctx, input.Name, project.ID)
if err == nil {
sendJSONError(w, "api-key with given name already exists",
"", http.StatusConflict)
@ -83,7 +88,7 @@ func (server *Server) addAPIKey(w http.ResponseWriter, r *http.Request) {
apikey := console.APIKeyInfo{
Name: input.Name,
ProjectID: projectUUID,
ProjectID: project.ID,
Secret: secret,
}
@ -248,10 +253,15 @@ func (server *Server) deleteAPIKeyByName(w http.ResponseWriter, r *http.Request)
return
}
projectUUID, err := uuid.FromString(projectUUIDString)
project, err := server.getProjectByAnyID(ctx, projectUUIDString)
if errors.Is(err, sql.ErrNoRows) {
sendJSONError(w, "project with specified uuid does not exist",
"", http.StatusNotFound)
return
}
if err != nil {
sendJSONError(w, "invalid project-uuid",
err.Error(), http.StatusBadRequest)
sendJSONError(w, "error getting project",
err.Error(), http.StatusInternalServerError)
return
}
@ -262,7 +272,7 @@ func (server *Server) deleteAPIKeyByName(w http.ResponseWriter, r *http.Request)
return
}
info, err := server.db.Console().APIKeys().GetByNameAndProjectID(ctx, apikeyName, projectUUID)
info, err := server.db.Console().APIKeys().GetByNameAndProjectID(ctx, apikeyName, project.ID)
if errors.Is(err, sql.ErrNoRows) {
sendJSONError(w, "API key with specified name does not exist",
"", http.StatusNotFound)
@ -293,10 +303,15 @@ func (server *Server) listAPIKeys(w http.ResponseWriter, r *http.Request) {
return
}
projectUUID, err := uuid.FromString(projectUUIDString)
project, err := server.getProjectByAnyID(ctx, projectUUIDString)
if errors.Is(err, sql.ErrNoRows) {
sendJSONError(w, "project with specified uuid does not exist",
"", http.StatusNotFound)
return
}
if err != nil {
sendJSONError(w, "invalid project-uuid",
err.Error(), http.StatusBadRequest)
sendJSONError(w, "error getting project",
err.Error(), http.StatusInternalServerError)
return
}
@ -304,7 +319,7 @@ func (server *Server) listAPIKeys(w http.ResponseWriter, r *http.Request) {
var apiKeys []console.APIKeyInfo
for i := uint(1); true; i++ {
page, err := server.db.Console().APIKeys().GetPagedByProjectID(
ctx, projectUUID, console.APIKeyCursor{
ctx, project.ID, console.APIKeyCursor{
Limit: apiKeysPerPage,
Page: i,
Order: console.KeyName,

View File

@ -0,0 +1,5 @@
node_modules
.DS_Store
.idea
/build/*
!/build/.keep

View File

@ -0,0 +1,57 @@
# essentials
## Project setup
```
# yarn
yarn
# npm
npm install
# pnpm
pnpm install
```
### Compiles and hot-reloads for development
```
# yarn
yarn dev
# npm
npm run dev
# pnpm
pnpm dev
```
### Compiles and minifies for production
```
# yarn
yarn build
# npm
npm run build
# pnpm
pnpm build
```
### Lints and fixes files
```
# yarn
yarn lint
# npm
npm run lint
# pnpm
pnpm lint
```
### Customize configuration
See [Configuration Reference](https://vitejs.dev/config/).

View File

@ -0,0 +1,25 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
//go:build !noembed
// +build !noembed
package backofficeui
import (
"embed"
"fmt"
"io/fs"
)
//go:embed all:build/*
var assets embed.FS
// Assets contains either the built admin/back-office/ui or it is empty.
var Assets = func() fs.FS {
build, err := fs.Sub(assets, "build")
if err != nil {
panic(fmt.Errorf("invalid embedding: %w", err))
}
return build
}()

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
//go:build noembed
// +build noembed
package backofficeui
import "io/fs"
// Assets contains either the built admin/back-office/ui or it is empty.
var Assets fs.FS = emptyFS{}
// emptyFS implements an empty filesystem
type emptyFS struct{}
// Open implements fs.FS method.
func (emptyFS) Open(name string) (fs.File, error) { return nil, fs.ErrNotExist }

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Storj - Admin</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
{
"name": "admin-ui",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --fix --ignore-path .gitignore"
},
"dependencies": {
"@fontsource-variable/inter": "^5.0.8",
"@mdi/font": "7.0.96",
"core-js": "^3.8.3",
"pinia": "^2.0.23",
"roboto-fontface": "*",
"vue": "^3.2.13",
"vue-router": "^4.1.6",
"vuetify": "^3.3.5",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.3",
"eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0",
"sass": "^1.63.6",
"vite": "^3.1.9",
"vite-plugin-vuetify": "^1.0.0-alpha.12"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -2,9 +2,9 @@
// See LICENSE for copying information.
<template>
<v-main>
<router-view />
</v-main>
</template>
<script setup lang="ts" />
<script setup>
//
</script>

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.83987 16.8886L1.47448 17.099C1.17636 17.1176 0.919588 16.891 0.900956 16.5929C0.899551 16.5704 0.899551 16.5479 0.900956 16.5254L1.11129 13.16C1.11951 13.0285 1.17546 12.9045 1.26864 12.8114L5.58927 8.49062L5.57296 8.43619C4.98999 6.44548 5.49345 4.26201 6.96116 2.72323L7.00936 2.67328L7.05933 2.62271C9.35625 0.325796 13.0803 0.325796 15.3772 2.62271C17.6741 4.91963 17.6741 8.64366 15.3772 10.9406C13.8503 12.4674 11.6456 13.0112 9.62856 12.4455L9.56357 12.4269L9.50918 12.4107L5.18856 16.7313C5.09538 16.8244 4.97139 16.8804 4.83987 16.8886ZM2.45229 15.5477L4.38997 15.4266L9.13372 10.6827L9.58862 10.864C11.2073 11.5091 13.072 11.1424 14.3255 9.88889C16.0416 8.17281 16.0416 5.39048 14.3255 3.6744C12.6094 1.95831 9.8271 1.95831 8.11101 3.6744C6.87177 4.91364 6.49924 6.7502 7.11424 8.3559L7.13584 8.41118L7.31711 8.86605L2.57342 13.61L2.45229 15.5477ZM10.7858 7.21411C11.3666 7.79494 12.3083 7.79494 12.8892 7.21411C13.47 6.63328 13.47 5.69157 12.8892 5.11074C12.3083 4.52991 11.3666 4.52991 10.7858 5.11074C10.205 5.69157 10.205 6.63328 10.7858 7.21411Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.06942 1.34998C11.7077 1.34998 13.8465 3.48875 13.8465 6.12706C13.8465 7.71017 13.0764 9.11342 11.8904 9.98265C14.2697 10.8565 16.1436 12.7789 16.9535 15.1905C16.9786 15.265 17.0026 15.34 17.0255 15.4154L17.0592 15.5289C17.2168 16.0738 16.9028 16.6434 16.3578 16.801C16.2651 16.8278 16.169 16.8414 16.0724 16.8414H1.91857C1.35598 16.8414 0.899902 16.3853 0.899902 15.8227C0.899902 15.7434 0.909173 15.6644 0.927488 15.5873L0.93956 15.5412C0.972672 15.4261 1.00818 15.3119 1.04602 15.1988C1.86483 12.7523 3.77818 10.8081 6.2038 9.94935C5.04316 9.0781 4.29233 7.69026 4.29233 6.12706C4.29233 3.48875 6.4311 1.34998 9.06942 1.34998ZM9.00117 10.9724C6.16785 10.9724 3.66499 12.7017 2.62214 15.264L2.59168 15.34H15.4107L15.4101 15.3388C14.3965 12.7624 11.9142 11.0092 9.09046 10.973L9.00117 10.9724ZM9.06942 2.85135C7.26029 2.85135 5.7937 4.31793 5.7937 6.12706C5.7937 7.93619 7.26029 9.40278 9.06942 9.40278C10.8785 9.40278 12.3451 7.93619 12.3451 6.12706C12.3451 4.31793 10.8785 2.85135 9.06942 2.85135Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5335 14.5335C10.2461 14.821 9.78432 14.828 9.48835 14.5546L9.46647 14.5335L1.221 6.28806C0.926335 5.9934 0.926335 5.51566 1.221 5.221C1.50847 4.93352 1.9702 4.92651 2.26617 5.19996L2.28806 5.221L9.99991 12.933L17.7119 5.221C17.9994 4.93352 18.4611 4.92651 18.7571 5.19996L18.779 5.221C19.0665 5.50847 19.0735 5.9702 18.8 6.26617L18.779 6.28806L10.5335 14.5335Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 493 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.9809 3.39991L11.0028 3.42094L14.9791 7.39723C15.2665 7.68463 15.2735 8.14625 15.0001 8.44216L14.9791 8.46404L11.0028 12.4403C10.7082 12.7349 10.2306 12.7349 9.93596 12.4403C9.64856 12.1529 9.64155 11.6913 9.91493 11.3954L9.93596 11.3735L12.6243 8.68493L1.55435 8.68498C1.13774 8.68498 0.800003 8.34725 0.800003 7.93063C0.800003 7.52418 1.12146 7.19281 1.52401 7.17688L1.55435 7.17628L12.6243 7.17623L9.93596 4.48775C9.64856 4.20034 9.64155 3.73872 9.91493 3.44282L9.93596 3.42094C10.2234 3.13353 10.685 3.12652 10.9809 3.39991Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 660 B

View File

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" fill="white" stroke="#EBEEF1"/>
<path d="M23.0565 6.96903C23.1425 7.03306 23.2123 7.11631 23.2603 7.21212C23.3084 7.30794 23.3334 7.41367 23.3333 7.52086V19.8959C23.3334 19.92 23.3321 19.9442 23.3297 19.9683C23.3649 20.692 23.1542 21.4064 22.7317 21.995C22.3093 22.5837 21.7 23.0121 21.0031 23.2103C20.3061 23.4086 19.5626 23.3651 18.8935 23.087C18.2245 22.8089 17.6693 22.3124 17.3183 21.6785C16.9673 21.0446 16.8413 20.3106 16.9606 19.5959C17.08 18.8812 17.4378 18.228 17.9757 17.7425C18.5137 17.2571 19.2001 16.9681 19.9232 16.9225C20.6464 16.8769 21.3637 17.0774 21.9583 17.4914V12.1115L14.1667 14.449V21.7292C14.1667 21.7534 14.1655 21.7776 14.163 21.8016C14.1983 22.5253 13.9875 23.2397 13.5651 23.8284C13.1426 24.4171 12.5333 24.8454 11.8364 25.0437C11.1395 25.242 10.3959 25.1985 9.72688 24.9203C9.05781 24.6422 8.50259 24.1458 8.15162 23.5118C7.80065 22.8779 7.67459 22.1439 7.79397 21.4292C7.91335 20.7145 8.27113 20.0613 8.80907 19.5759C9.34701 19.0905 10.0334 18.8014 10.7566 18.7558C11.4797 18.7102 12.197 18.9108 12.7917 19.3248V10.2709C12.7917 10.1231 12.8393 9.97933 12.9275 9.86078C13.0157 9.74224 13.1397 9.65524 13.2812 9.6127L22.4478 6.8627C22.5505 6.8317 22.6591 6.82517 22.7648 6.84363C22.8705 6.8621 22.9704 6.90504 23.0565 6.96903ZM14.1667 13.0135L21.9583 10.676V8.44486L14.1667 10.7824V13.0135ZM10.9583 20.125C10.4721 20.125 10.0058 20.3182 9.66196 20.662C9.31814 21.0058 9.12499 21.4721 9.12499 21.9584C9.12499 22.4446 9.31814 22.9109 9.66196 23.2547C10.0058 23.5985 10.4721 23.7917 10.9583 23.7917C11.4446 23.7917 11.9109 23.5985 12.2547 23.2547C12.5985 22.9109 12.7917 22.4446 12.7917 21.9584C12.7917 21.4721 12.5985 21.0058 12.2547 20.662C11.9109 20.3182 11.4446 20.125 10.9583 20.125ZM18.2917 20.125C18.2917 20.6113 18.4848 21.0776 18.8286 21.4214C19.1724 21.7652 19.6388 21.9584 20.125 21.9584C20.6112 21.9584 21.0775 21.7652 21.4214 21.4214C21.7652 21.0776 21.9583 20.6113 21.9583 20.125C21.9583 19.6388 21.7652 19.1725 21.4214 18.8287C21.0775 18.4848 20.6112 18.2917 20.125 18.2917C19.6388 18.2917 19.1724 18.4848 18.8286 18.8287C18.4848 19.1725 18.2917 19.6388 18.2917 20.125Z" fill="#FF8A00"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="8" fill="#FFA800" fill-opacity="0.1"/>
<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" stroke="#FFA800" stroke-opacity="0.2"/>
<path d="M23.0565 6.96903C23.1425 7.03306 23.2123 7.11631 23.2603 7.21212C23.3084 7.30794 23.3334 7.41367 23.3333 7.52086V19.8959C23.3334 19.92 23.3321 19.9442 23.3297 19.9683C23.3649 20.692 23.1542 21.4064 22.7317 21.995C22.3093 22.5837 21.7 23.0121 21.0031 23.2103C20.3061 23.4086 19.5626 23.3651 18.8936 23.087C18.2245 22.8089 17.6693 22.3124 17.3183 21.6785C16.9673 21.0446 16.8413 20.3106 16.9606 19.5959C17.08 18.8812 17.4378 18.228 17.9757 17.7425C18.5137 17.2571 19.2001 16.9681 19.9232 16.9225C20.6464 16.8769 21.3637 17.0774 21.9583 17.4914V12.1115L14.1667 14.449V21.7292C14.1667 21.7534 14.1655 21.7776 14.163 21.8016C14.1983 22.5253 13.9875 23.2397 13.5651 23.8284C13.1426 24.4171 12.5333 24.8454 11.8364 25.0437C11.1395 25.242 10.396 25.1985 9.72688 24.9203C9.05782 24.6422 8.5026 24.1458 8.15163 23.5118C7.80066 22.8779 7.6746 22.1439 7.79398 21.4292C7.91336 20.7145 8.27114 20.0613 8.80908 19.5759C9.34702 19.0905 10.0334 18.8014 10.7566 18.7558C11.4797 18.7102 12.197 18.9108 12.7917 19.3248V10.2709C12.7917 10.1231 12.8393 9.97933 12.9275 9.86078C13.0157 9.74224 13.1397 9.65524 13.2812 9.6127L22.4478 6.8627C22.5505 6.8317 22.6591 6.82517 22.7648 6.84363C22.8705 6.8621 22.9704 6.90504 23.0565 6.96903V6.96903ZM14.1667 13.0135L21.9583 10.676V8.44486L14.1667 10.7824V13.0135ZM10.9583 20.125C10.4721 20.125 10.0058 20.3182 9.66197 20.662C9.31815 21.0058 9.125 21.4721 9.125 21.9584C9.125 22.4446 9.31815 22.9109 9.66197 23.2547C10.0058 23.5985 10.4721 23.7917 10.9583 23.7917C11.4446 23.7917 11.9109 23.5985 12.2547 23.2547C12.5985 22.9109 12.7917 22.4446 12.7917 21.9584C12.7917 21.4721 12.5985 21.0058 12.2547 20.662C11.9109 20.3182 11.4446 20.125 10.9583 20.125ZM18.2917 20.125C18.2917 20.6113 18.4848 21.0776 18.8286 21.4214C19.1725 21.7652 19.6388 21.9584 20.125 21.9584C20.6112 21.9584 21.0775 21.7652 21.4214 21.4214C21.7652 21.0776 21.9583 20.6113 21.9583 20.125C21.9583 19.6388 21.7652 19.1725 21.4214 18.8287C21.0775 18.4848 20.6112 18.2917 20.125 18.2917C19.6388 18.2917 19.1725 18.4848 18.8286 18.8287C18.4848 19.1725 18.2917 19.6388 18.2917 20.125Z" fill="#FFA800"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="8" fill="#537CFF" fill-opacity="0.1"/>
<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" stroke="#537CFF" stroke-opacity="0.2"/>
<path d="M11.6989 16.4802C11.4402 16.2215 11.4339 15.8059 11.68 15.5395L11.6989 15.5198L19.1198 8.09892C19.385 7.83373 19.815 7.83373 20.0802 8.09892C20.3389 8.35765 20.3452 8.77321 20.0991 9.03958L20.0802 9.05928L13.1393 15.9999L20.0802 22.9408C20.3389 23.1995 20.3452 23.6151 20.0991 23.8814L20.0802 23.9011C19.8215 24.1599 19.4059 24.1662 19.1395 23.9201L19.1198 23.9011L11.6989 16.4802Z" fill="#537CFF"/>
</svg>

After

Width:  |  Height:  |  Size: 678 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.9519 0.900024C14.9905 0.900024 15.8325 1.74202 15.8325 2.78067V16.5214C15.8325 16.8409 15.5734 17.1 15.2539 17.1C15.0952 17.1 14.9436 17.0349 14.8343 16.9199L9.44551 11.2481C9.11532 10.9006 8.56592 10.8865 8.21839 11.2167C8.20766 11.2269 8.19719 11.2374 8.18699 11.2481L2.79816 16.9199C2.57803 17.1516 2.21176 17.161 1.98007 16.9409C1.86509 16.8316 1.79999 16.68 1.79999 16.5214V2.78067C1.79999 1.74202 2.64198 0.900024 3.68064 0.900024H13.9519ZM13.9519 2.49134H3.68064C3.52811 2.49134 3.40314 2.60937 3.3921 2.75908L3.39131 2.78067V13.9854L7.03335 10.152C7.04779 10.1368 7.06243 10.1218 7.07726 10.107L7.1223 10.0631C8.09402 9.13985 9.62275 9.16649 10.5619 10.1137L10.5992 10.152L14.2412 13.9853V2.78067C14.2412 2.62814 14.1232 2.50318 13.9735 2.49214L13.9519 2.49134Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 904 B

View File

@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" fill="white" stroke="#EBEEF1"/>
<path d="M10 12L16 13.2L22 12L20 22.5L18.5 23L16 23.4L13.5 23L12 22.5L10 12Z" fill="#D7E8FF"/>
<ellipse cx="16" cy="11" rx="6" ry="2" fill="white"/>
<path d="M23.3549 14.5467C24.762 15.9538 24.7748 18.227 23.3778 19.624C22.8117 20.1901 22.1018 20.5247 21.3639 20.6289L21.1106 22.0638C21.092 23.1897 18.7878 24.1 15.9481 24.1C13.1254 24.1 10.8318 23.2006 10.7863 22.0841L10.7858 22.0638L8.84465 11.066C8.8228 10.9882 8.80882 10.9095 8.80303 10.8299L8.79999 10.8122L8.80189 10.8123C8.80062 10.7903 8.79999 10.7682 8.79999 10.746C8.79999 9.17422 12.0003 7.90002 15.9481 7.90002C19.8959 7.90002 23.0962 9.17422 23.0962 10.746C23.0962 10.7682 23.0955 10.7903 23.0943 10.8123L23.0962 10.8122L23.093 10.8311C23.0872 10.9098 23.0734 10.9876 23.0519 11.0645L22.5749 13.7666L23.3549 14.5467ZM21.2961 12.6344C19.9867 13.2218 18.076 13.592 15.9481 13.592C13.8203 13.592 11.9096 13.2219 10.6001 12.6344L12.0072 20.6077L12.2373 21.8286L12.2586 21.8452C12.3789 21.9354 12.5651 22.0371 12.807 22.1351L12.8561 22.1546C13.6355 22.4594 14.7462 22.6439 15.9481 22.6439C17.1569 22.6439 18.2733 22.4573 19.0527 22.1497C19.3337 22.0388 19.5431 21.9223 19.6661 21.8231L19.6761 21.8148L19.9019 20.5348C19.3338 20.3787 18.7955 20.0812 18.3429 19.6429L18.3004 19.6011L15.3749 16.6756C15.0906 16.3913 15.0906 15.9303 15.3749 15.646C15.6523 15.3686 16.0978 15.3618 16.3834 15.6257L16.4045 15.646L19.33 18.5715C19.5717 18.8132 19.8555 18.9861 20.1569 19.0901L21.2961 12.6344ZM22.2661 15.517L21.6408 19.0597C21.8989 18.9575 22.1402 18.8024 22.3482 18.5944C23.1641 17.7784 23.1664 16.4494 22.3549 15.6065L22.3253 15.5763L22.2661 15.517ZM15.9481 9.35612C14.2013 9.35612 12.5813 9.62893 11.4322 10.0864C10.9385 10.283 10.5712 10.4995 10.3598 10.6985C10.3463 10.7112 10.334 10.7232 10.3228 10.7347L10.3122 10.7459L10.3314 10.7661L10.3598 10.7936C10.5712 10.9926 10.9385 11.2091 11.4322 11.4056C12.5813 11.8631 14.2013 12.1359 15.9481 12.1359C17.6949 12.1359 19.3149 11.8631 20.4639 11.4056C20.9576 11.2091 21.325 10.9926 21.5364 10.7936C21.5499 10.7809 21.5622 10.7688 21.5733 10.7574L21.5841 10.7459L21.5647 10.726L21.5364 10.6985C21.325 10.4995 20.9576 10.283 20.4639 10.0864C19.3149 9.62893 17.6949 9.35612 15.9481 9.35612Z" fill="#0149FF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,6 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="8" fill="#0149FF" fill-opacity="0.05"/>
<rect x="0.5" y="0.5" width="31" height="31" rx="7.5" stroke="#0149FF" stroke-opacity="0.2"/>
<path d="M10 12L16 13.2L22 12L20 22.5L18.5 23L16 23.4L13.5 23L12 22.5L10 12Z" fill="#0149FF" fill-opacity="0.2"/>
<path d="M23.3549 14.5467C24.762 15.9538 24.7748 18.227 23.3778 19.624C22.8117 20.1901 22.1018 20.5247 21.3639 20.6289L21.1106 22.0638C21.092 23.1897 18.7878 24.1 15.9481 24.1C13.1254 24.1 10.8319 23.2006 10.7863 22.0841L10.7858 22.0638L8.84466 11.066C8.82281 10.9882 8.80883 10.9095 8.80304 10.8299L8.8 10.8122L8.8019 10.8123C8.80063 10.7903 8.8 10.7682 8.8 10.746C8.8 9.17422 12.0003 7.90002 15.9481 7.90002C19.8959 7.90002 23.0962 9.17422 23.0962 10.746C23.0962 10.7682 23.0955 10.7903 23.0943 10.8123L23.0962 10.8122L23.093 10.8311C23.0872 10.9098 23.0734 10.9876 23.0519 11.0645L22.5749 13.7666L23.3549 14.5467ZM21.2962 12.6344C19.9867 13.2218 18.076 13.592 15.9481 13.592C13.8203 13.592 11.9096 13.2219 10.6001 12.6344L12.0072 20.6077L12.2373 21.8286L12.2586 21.8452C12.3789 21.9354 12.5652 22.0371 12.807 22.1351L12.8561 22.1546C13.6355 22.4594 14.7462 22.6439 15.9481 22.6439C17.1569 22.6439 18.2733 22.4573 19.0528 22.1497C19.3337 22.0388 19.5431 21.9223 19.6661 21.8231L19.6761 21.8148L19.9019 20.5348C19.3338 20.3787 18.7955 20.0812 18.3429 19.6429L18.3004 19.6011L15.3749 16.6756C15.0906 16.3913 15.0906 15.9303 15.3749 15.646C15.6523 15.3686 16.0978 15.3618 16.3834 15.6257L16.4045 15.646L19.33 18.5715C19.5717 18.8132 19.8555 18.9861 20.1569 19.0901L21.2962 12.6344ZM22.2661 15.517L21.6408 19.0597C21.8989 18.9575 22.1402 18.8024 22.3482 18.5944C23.1641 17.7784 23.1664 16.4494 22.355 15.6065L22.3253 15.5763L22.2661 15.517ZM15.9481 9.35612C14.2013 9.35612 12.5813 9.62893 11.4322 10.0864C10.9385 10.283 10.5712 10.4995 10.3598 10.6985C10.3463 10.7112 10.334 10.7232 10.3228 10.7347L10.3122 10.7459L10.3314 10.7661L10.3598 10.7936C10.5712 10.9926 10.9385 11.2091 11.4322 11.4056C12.5813 11.8631 14.2013 12.1359 15.9481 12.1359C17.6949 12.1359 19.3149 11.8631 20.4639 11.4056C20.9577 11.2091 21.325 10.9926 21.5364 10.7936C21.5499 10.7809 21.5622 10.7688 21.5733 10.7574L21.5841 10.7459L21.5647 10.726L21.5364 10.6985C21.325 10.4995 20.9577 10.283 20.4639 10.0864C19.3149 9.62893 17.6949 9.35612 15.9481 9.35612Z" fill="#0149FF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.3549 7.54666C17.762 8.95376 17.7749 11.227 16.3778 12.624C15.8118 13.1901 15.1018 13.5247 14.3639 13.6289L14.1106 15.0638C14.092 16.1897 11.7878 17.1 8.94814 17.1C6.12547 17.1 3.83191 16.2006 3.78632 15.0841L3.78589 15.0638L1.84471 4.06597C1.82286 3.98819 1.80888 3.90946 1.8031 3.82992L1.80005 3.81221L1.80195 3.81231C1.80068 3.79028 1.80005 3.76818 1.80005 3.74602C1.80005 2.17422 5.00036 0.900024 8.94814 0.900024C12.8959 0.900024 16.0962 2.17422 16.0962 3.74602C16.0962 3.76818 16.0956 3.79028 16.0943 3.81231L16.0962 3.81221L16.0931 3.83111C16.0873 3.90975 16.0735 3.98759 16.052 4.06451L15.5749 6.76662L16.3549 7.54666ZM14.2962 5.63437C12.9868 6.22183 11.076 6.59202 8.94814 6.59202C6.82032 6.59202 4.90965 6.22185 3.6002 5.63443L5.00729 13.6077L5.23735 14.8286L5.25867 14.8452C5.37899 14.9354 5.56521 15.0371 5.80702 15.1351L5.85612 15.1546C6.63558 15.4594 7.74625 15.6439 8.94814 15.6439C10.157 15.6439 11.2733 15.4573 12.0528 15.1497C12.3338 15.0388 12.5432 14.9223 12.6661 14.8231L12.6761 14.8148L12.902 13.5348C12.3339 13.3787 11.7956 13.0812 11.3429 12.6429L11.3005 12.6011L8.37494 9.67559C8.09062 9.39127 8.09062 8.93029 8.37494 8.64597C8.65232 8.36859 9.09785 8.36182 9.38344 8.62568L9.40455 8.64597L12.3301 11.5715C12.5718 11.8132 12.8556 11.9861 13.157 12.0901L14.2962 5.63437ZM15.2661 8.51698L14.6409 12.0597C14.899 11.9575 15.1403 11.8024 15.3482 11.5944C16.1642 10.7784 16.1664 9.44942 15.355 8.60652L15.3253 8.57627L15.2661 8.51698ZM8.94814 2.35612C7.20131 2.35612 5.58131 2.62893 4.43229 3.08641C3.93857 3.28298 3.57123 3.49947 3.35982 3.69848C3.34635 3.71116 3.33405 3.72325 3.32289 3.73469L3.31227 3.74589L3.33148 3.76606L3.35982 3.79357C3.57123 3.99258 3.93857 4.20906 4.43229 4.40564C5.58131 4.86312 7.20131 5.13593 8.94814 5.13593C10.695 5.13593 12.315 4.86312 13.464 4.40564C13.9577 4.20906 14.325 3.99258 14.5365 3.79357C14.5499 3.78089 14.5622 3.7688 14.5734 3.75735L14.5841 3.74589L14.5648 3.72599L14.5365 3.69848C14.325 3.49947 13.9577 3.28298 13.464 3.08641C12.315 2.62893 10.695 2.35612 8.94814 2.35612Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.6971 2.69995L14.8103 2.70051C15.5609 2.70821 15.8499 2.79454 16.1411 2.95031C16.4466 3.11366 16.6863 3.35339 16.8496 3.65884L16.8719 3.7015C17.0209 3.99363 17.1 4.30614 17.1 5.1028V12.2128C17.1 13.0483 17.013 13.3513 16.8496 13.6568C16.6863 13.9622 16.4466 14.2019 16.1411 14.3653L16.0984 14.3876C15.8063 14.5366 15.4938 14.6157 14.6971 14.6157H3.30284L3.18966 14.6151C2.43913 14.6074 2.15013 14.5211 1.85888 14.3653C1.55343 14.2019 1.31371 13.9622 1.15035 13.6568L1.12805 13.6141C0.979085 13.322 0.899994 13.0095 0.899994 12.2128V5.1028L0.900557 4.98962C0.908252 4.23909 0.994587 3.95009 1.15035 3.65884C1.31371 3.35339 1.55343 3.11366 1.85888 2.95031L1.90154 2.92801C2.19367 2.77904 2.50618 2.69995 3.30284 2.69995H14.6971ZM15.6273 8.72474H2.37286L2.37291 12.2988C2.37509 12.7639 2.39618 12.8634 2.44902 12.9622C2.47512 13.011 2.50462 13.0405 2.55342 13.0666L2.57984 13.0799C2.66664 13.1203 2.77736 13.1384 3.13851 13.1422L3.30284 13.1429L14.7831 13.1427C15.2483 13.1406 15.3478 13.1195 15.4466 13.0666C15.4954 13.0405 15.5249 13.011 15.551 12.9622L15.5642 12.9358C15.6102 12.837 15.6273 12.7073 15.6273 12.2128V8.72474ZM3.25888 4.17273C2.75755 4.17384 2.65513 4.19458 2.55342 4.24898C2.50462 4.27508 2.47512 4.30457 2.44902 4.35338L2.43576 4.3798C2.39539 4.4666 2.37729 4.57732 2.3735 4.93847L2.37272 5.1028V6.31483H15.6273V5.05884C15.6261 4.55751 15.6054 4.45509 15.551 4.35338C15.5249 4.30457 15.4954 4.27508 15.4466 4.24898L15.4201 4.23572C15.3274 4.19256 15.2072 4.17486 14.7831 4.17287L3.25888 4.17273Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.0566 3.47049C14.4084 3.82234 14.417 4.38746 14.0823 4.74971L14.0566 4.77649L7.13827 11.6948C7.00266 11.8304 6.8361 11.9169 6.66103 11.9539C6.35998 12.0545 6.01478 11.991 5.76673 11.7589L5.75596 11.7487L1.87048 7.86338C1.50984 7.50274 1.50984 6.91803 1.87048 6.55739C2.22232 6.20555 2.78744 6.19696 3.14969 6.53165L3.17647 6.55739L6.42021 9.80089L12.7506 3.47049C13.1112 3.10985 13.696 3.10985 14.0566 3.47049Z" fill="#00D36E"/>
</svg>

After

Width:  |  Height:  |  Size: 544 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.8429 1.15699C17.1857 1.49977 17.1857 2.05553 16.8429 2.39831L10.2412 8.99979L16.7865 15.5451C17.1293 15.8878 17.1293 16.4436 16.7865 16.7864C16.4437 17.1292 15.888 17.1292 15.5452 16.7864L8.99991 10.2411L2.39844 16.8428C2.05565 17.1856 1.49989 17.1856 1.15711 16.8428C0.814329 16.5 0.814329 15.9443 1.15711 15.6015L7.7587 8.99968L1.21353 2.45474C0.870753 2.11195 0.870753 1.5562 1.21353 1.21341C1.55632 0.870631 2.11208 0.870631 2.45486 1.21341L9.00002 7.75835L15.6016 1.15699C15.9444 0.814207 16.5002 0.814207 16.8429 1.15699Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 700 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.9999 2.69995C13.4734 2.69995 17.0999 6.29647 17.0999 10.733C17.0999 12.3941 16.5915 13.9374 15.7207 15.2183H2.27915C1.40828 13.9374 0.899902 12.3941 0.899902 10.733C0.899902 6.29647 4.5264 2.69995 8.9999 2.69995ZM8.9999 4.17268C5.33704 4.17268 2.37263 7.11259 2.37263 10.733C2.37263 11.765 2.61287 12.7593 3.06475 13.656L3.11087 13.7455H14.8889L14.8921 13.7398C15.3587 12.8462 15.6132 11.8525 15.6266 10.8192L15.6272 10.733C15.6272 7.11259 12.6628 4.17268 8.9999 4.17268ZM11.7821 8.02523C12.0678 8.29853 12.0855 8.74671 11.8279 9.04146L11.8052 9.06634L9.53203 11.4428C9.25092 11.7367 8.7848 11.7471 8.49091 11.466C8.20519 11.1927 8.18746 10.7445 8.44502 10.4497L8.46778 10.4248L10.7409 8.04836C11.022 7.75448 11.4882 7.74412 11.7821 8.02523Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 875 B

View File

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.128 3.60013C14.9368 3.60427 15.3608 3.6899 15.8016 3.92564C16.2192 4.14899 16.551 4.48078 16.7743 4.89841C17.0161 5.35051 17.1 5.78485 17.1 6.63492V11.9189C17.0957 12.7277 17.0101 13.1517 16.7743 13.5924C16.551 14.0101 16.2192 14.3419 15.8016 14.5652C15.3721 14.7949 14.9586 14.8821 14.1895 14.8902L14.0651 14.8909H3.93493C3.08487 14.8909 2.65053 14.807 2.19843 14.5652C1.7808 14.3419 1.44901 14.0101 1.22565 13.5924C0.995959 13.163 0.908776 12.7495 0.900632 11.9804L0.899994 11.8559V6.63492C0.899994 5.78485 0.983869 5.35051 1.22565 4.89841C1.44901 4.48078 1.7808 4.14899 2.19843 3.92564C2.65053 3.68385 3.08487 3.59998 3.93493 3.59998L14.128 3.60013ZM8.26363 4.94998L3.88478 4.95007C3.31066 4.95218 3.08426 4.99165 2.87239 5.09683L2.83509 5.11608C2.65272 5.21361 2.51363 5.3527 2.4161 5.53507C2.29321 5.76486 2.24999 5.98866 2.24999 6.63492V11.9061C2.2522 12.4811 2.2918 12.7073 2.39735 12.9195L2.4161 12.9558C2.51363 13.1382 2.65272 13.2772 2.83509 13.3748C3.06487 13.4977 3.28867 13.5409 3.93493 13.5409H8.26363V4.94998ZM14.1152 4.95007L9.61363 4.94998V13.5408L14.1152 13.5408C14.6728 13.5387 14.9024 13.5014 15.1093 13.4029L15.1286 13.3935L15.1649 13.3748C15.3473 13.2772 15.4864 13.1382 15.5839 12.9558C15.7068 12.726 15.75 12.5022 15.75 11.8559L15.7499 6.58476C15.7478 6.01064 15.7083 5.78424 15.6031 5.57238L15.5839 5.53507C15.4864 5.3527 15.3473 5.21361 15.1649 5.11608L15.1451 5.10567L15.1286 5.09734C15.1111 5.08865 15.0936 5.08041 15.0758 5.0726C15.074 5.07185 15.0722 5.07107 15.0704 5.07029L15.0758 5.0726C15.0709 5.07049 15.0661 5.0684 15.0612 5.06635L15.0704 5.07029C15.0625 5.06688 15.0546 5.06355 15.0465 5.0603L15.0612 5.06635C15.0542 5.0634 15.0471 5.06052 15.04 5.0577L15.0465 5.0603C15.0423 5.05859 15.038 5.0569 15.0338 5.05524L15.04 5.0577C15.0267 5.0524 15.0131 5.04733 14.9992 5.04247C14.9969 5.04166 14.9945 5.04084 14.9921 5.04004L14.9992 5.04247C14.9909 5.03956 14.9824 5.03672 14.9739 5.03396C14.9476 5.02548 14.9201 5.01774 14.8911 5.01068C14.8871 5.00971 14.8831 5.00876 14.8791 5.00782L14.8911 5.01068C14.8824 5.00857 14.8736 5.00651 14.8646 5.00453L14.8791 5.00782C14.8713 5.006 14.8634 5.00423 14.8554 5.00251L14.8646 5.00453C14.8565 5.00272 14.8482 5.00096 14.8397 4.99926L14.8554 5.00251C14.848 5.00094 14.8406 4.99941 14.833 4.99791L14.8397 4.99926C14.8154 4.99433 14.79 4.98982 14.7632 4.9857C14.7582 4.98493 14.7531 4.98418 14.7481 4.98344L14.7632 4.9857C14.7548 4.98441 14.7462 4.98316 14.7376 4.98194L14.7481 4.98344C14.7246 4.98004 14.7002 4.97692 14.6746 4.97408C14.6659 4.97311 14.657 4.97217 14.648 4.97127C14.6355 4.97001 14.6228 4.96882 14.6098 4.96769C14.5937 4.96629 14.5772 4.96498 14.5603 4.96375C14.5552 4.96339 14.5501 4.96303 14.5449 4.96268L14.5603 4.96375C14.5479 4.96286 14.5354 4.96202 14.5225 4.96122L14.5449 4.96268C14.5333 4.96189 14.5215 4.96114 14.5095 4.96044L14.5225 4.96122C14.5116 4.96055 14.5006 4.9599 14.4893 4.95929L14.5095 4.96044C14.4818 4.9588 14.4528 4.95736 14.4225 4.95612C14.4214 4.95608 14.4202 4.95603 14.4189 4.95598L14.4225 4.95612C14.4109 4.95565 14.399 4.9552 14.387 4.95478L14.4189 4.95598C14.3749 4.9542 14.3281 4.95284 14.2784 4.95187C14.2563 4.95144 14.2337 4.95108 14.2104 4.9508L14.1152 4.95007ZM6.29999 10.7795C6.67279 10.7795 6.97499 11.0817 6.97499 11.4545C6.97499 11.8182 6.68735 12.1147 6.32714 12.129L6.29999 12.1295H4.0909C3.71811 12.1295 3.4159 11.8273 3.4159 11.4545C3.4159 11.0908 3.70355 10.7943 4.06375 10.7801L4.0909 10.7795H6.29999ZM13.1114 10.7795C13.4841 10.7795 13.7864 11.0817 13.7864 11.4545C13.7864 11.8182 13.4987 12.1147 13.1385 12.129L13.1114 12.1295H11.5159C11.1431 12.1295 10.8409 11.8273 10.8409 11.4545C10.8409 11.0908 11.1285 10.7943 11.4888 10.7801L11.5159 10.7795H13.1114ZM6.29999 8.57043C6.67279 8.57043 6.97499 8.87264 6.97499 9.24543C6.97499 9.60913 6.68735 9.90565 6.32714 9.91989L6.29999 9.92043H4.0909C3.71811 9.92043 3.4159 9.61822 3.4159 9.24543C3.4159 8.88173 3.70355 8.58521 4.06375 8.57097L4.0909 8.57043H6.29999ZM13.725 8.57043C14.0978 8.57043 14.4 8.87264 14.4 9.24543C14.4 9.60913 14.1123 9.90565 13.7521 9.91989L13.725 9.92043H11.5159C11.1431 9.92043 10.8409 9.61822 10.8409 9.24543C10.8409 8.88173 11.1285 8.58521 11.4888 8.57097L11.5159 8.57043H13.725ZM6.29999 6.36134C6.67279 6.36134 6.97499 6.66355 6.97499 7.03634C6.97499 7.40004 6.68735 7.69656 6.32714 7.7108L6.29999 7.71134H4.0909C3.71811 7.71134 3.4159 7.40913 3.4159 7.03634C3.4159 6.67264 3.70355 6.37612 4.06375 6.36188L4.0909 6.36134H6.29999ZM13.725 6.36134C14.0978 6.36134 14.4 6.66355 14.4 7.03634C14.4 7.40004 14.1123 7.69656 13.7521 7.7108L13.725 7.71134H11.5159C11.1431 7.71134 10.8409 7.40913 10.8409 7.03634C10.8409 6.67264 11.1285 6.37612 11.4888 6.36188L11.5159 6.36134H13.725Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.6275 16.4385L11.5989 16.4109L6.51469 11.3268C6.1286 10.9407 6.1286 10.3147 6.51469 9.92861C6.89136 9.55194 7.49635 9.54276 7.88416 9.90105L7.91283 9.92861L11.3423 13.3582V2.18835C11.3423 1.64234 11.785 1.19971 12.331 1.19971C12.8637 1.19971 13.298 1.62101 13.3188 2.14858L13.3196 2.18835V13.2924L16.683 9.92861C17.0597 9.55194 17.6647 9.54276 18.0525 9.90105L18.0812 9.92861C18.4579 10.3053 18.467 10.9103 18.1087 11.2981L18.0812 11.3268L12.997 16.4109C12.6203 16.7876 12.0153 16.7968 11.6275 16.4385ZM23.3012 20.8529C23.3012 21.399 22.8585 21.8416 22.3125 21.8416H2.71949C2.17348 21.8416 1.73085 21.399 1.73085 20.8529C1.73085 20.3069 2.17348 19.8643 2.71949 19.8643H22.3125C22.8585 19.8643 23.3012 20.3069 23.3012 20.8529ZM2.68981 21.7665C2.1438 21.7665 1.70117 21.3239 1.70117 20.7779V16.8233C1.70117 16.2773 2.1438 15.8347 2.68981 15.8347C3.23582 15.8347 3.67845 16.2773 3.67845 16.8233V20.7779C3.67845 21.3239 3.23582 21.7665 2.68981 21.7665ZM22.2829 21.7665C21.7368 21.7665 21.2942 21.3239 21.2942 20.7779V16.8233C21.2942 16.2773 21.7368 15.8347 22.2829 15.8347C22.8289 15.8347 23.2715 16.2773 23.2715 16.8233V20.7779C23.2715 21.3239 22.8289 21.7665 22.2829 21.7665Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,3 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.26677 7.26676C5.12303 7.4105 4.89216 7.41401 4.74418 7.27728L4.73323 7.26676L0.610498 3.14403C0.463168 2.9967 0.463168 2.75783 0.610498 2.6105C0.754235 2.46676 0.985101 2.46325 1.13309 2.59998L1.14403 2.6105L4.99996 6.46651L8.85597 2.6105C8.99971 2.46676 9.23057 2.46326 9.37856 2.59998L9.3895 2.6105C9.53324 2.75423 9.53674 2.9851 9.40002 3.13309L9.3895 3.14403L5.26677 7.26676Z" fill="#56606D"/>
</svg>

After

Width:  |  Height:  |  Size: 513 B

Some files were not shown because too many files have changed in this diff Show More