smallbets / userbase Goto Github PK
View Code? Open in Web Editor NEWCreate secure and private web apps using only static JavaScript, HTML, and CSS.
Home Page: https://userbase.com
License: MIT License
Create secure and private web apps using only static JavaScript, HTML, and CSS.
Home Page: https://userbase.com
License: MIT License
I get an Uncaught SyntaxError: Unexpected token ','
error when I do…
userbase.openDatabase({'…', function(items) {…}})
But not when I replace it with…
userbase.openDatabase({databaseName: '…', changeHandler: function(items) {…}})
(I'm using https://sdk.userbase.com/1/userbase.js
.)
Userbase looks promising and definitely satisfies a need in front-end development.
However, I noticed the client library is massive: ~70K gzipped. Much of which comes from @babel/runtime-corejs3
.
In general, client libraries don't ship polyfills. Instead, they note browser support in the README and let consumers polyfill as needed. Not reason every consumer should have to bear the burden of @babel/runtime-corejs3
.
Likewise with axios
. It's not much compared to @babel/runtime-corejs3
, but 4K isn't nothing. And it's certainly not carrying its own weight given it's used only in api/auth.js
. Consider using fetch
directly and—again—letting the consumer polyfill as needed.
Perhaps all of this known and temporary. If so, please disregard.
Sometimes it could be useful, similar to user profiles, to store unencrypted meta data in a database. An example would be a self implemented 1-n relation where the n databases have different user permissions.
A real world example would be:
There are organizations and each organization has different users with different permissions. However, it is possible for users to be part of multiple organizations.
If I now want to create teams within the organization, which include different users of the organization with different permissions, I have a problem.
Now, to get the teams of a user within an organization, I would either have to choose database names that contain the organization, or I would have to open ALL team databases of a user and open a team entry to filter out the right teams. Since database names are limited to 50 characters, it is not easy to ensure collision free names in the first approach. With the second approach I see serious performance issues in the long run.
One idea to solve this would be to store the organization ID as unencrypted meta data, so that you can easily filter for this meta data with getDatabases()
without opening every database first.
Is there a way to single sign on using a link?
When uploading large files, it would be useful to be able to track the progress of the upload (especially for progress bars). Maybe the uploadFile function could get a handler which returns the current progress in certain intervals.
Example API:
const exampleFile = new File(['example'], 'example.txt', { type: 'text/plain' })
userbase.uploadFile({
databaseName: 'example-database-name',
itemId: 'example-id',
file: exampleFile,
progressHandler: (bytesTransferred) => console.log(bytesTransferred / exampleFile.size) // => would log percentage
}).then(() => {
// file stored durably
}).catch((e) => console.error(e))
User Story
As a developer, I want to be able to store data that is not user specific and is used across my application.
Example
A coding interview platform.
I canceled a user via the Stripe dashboard, which yields subscriptionStatus: "canceled"
in the userbase profile.
If I attempt to purchaseSubscription
it returns the following error:
StripeError: You may only specify one of these parameters: customer, customer_email.
For generic changeHandlers which listen to multiple databases it is helpful to know from which database the items were read.
To reproduce
userbase.getDatabases()
but it fails with a ServiceUnavailable error.It might be useful to allow users to revoke their own database access from shared databases. A scenario for this would be: A user can be part of several teams (each team has one database) and wants to leave one team. In this case, he would have to tell a user with resharing permissions that he wants to leave the team.
In Firefox, invoking the signIn
method leads to a WebSocket error:
The same code works in Chrome and Safari.
This happens with a create-react-app project, on http://localhost:3000
Versions used:
userbase-js 2.0.0
Firefox Developer Edition 78.0b9
Firefox 77.0.1
When I try to change password with
userbase.updateUser({
currentPassword: 'example-password',
newPassword: 'example-new-password'
})
If I enter the right currentPassword, it works well and It changes the password with the new one.
But if I enter the wrong password i receive this error "CurrentPasswordIncorrect", and it seems that it terminates the current session. If I retry to do another request I receive the error: UserNotSignedIn and I need to login again.
When trying to start a fresh App with userbase-js
as a dependency, I get the following error from Vite:
When starting my app with userbase-js
as a dev dependency:
import userbase from 'userbase-js';
or import * as userbase from 'userbase-js';
import userbase from '../../node_modules/userbase-js/dist/userbase.js';
import * as userbase from '../../node_modules/userbase-js/dist/userbase.js';
I found a reference in Vite issue #728, but none of the fixes there seemed to help.
I've been stuck on this for days now, and I don't seem to be getting any closer to figuring it out. Any advice would be greatly appreciated!
For our product we offer both monthly and yearly plans.
It would be great if this was supported by the Userbase payment feature.
I apologize if this functionally is already included, I just haven't found it in the documentation.
At the moment, the openDatabase
response does not provide any information on whether the database has been shared, who it's shared with, who owns it, etc.
Similar to getDatabases
, could you return the same data for openDatabase
? This data would be:
This might be a shot in the dark, but I have had a hard time searching for userbase as a project name. It gives many, many unrelated results on google 😉
So I don't know whether you would consider changing to a less generic project name, but that would help people trying to do research or find docs quickly.
Hi,
I've got a question/suggestion about the provided types.
10 functions require at least databaseName or databaseId
https://github.com/smallbets/userbase/blob/master/src/userbase-js/types/index.d.ts#L123-L145
The Problem
They're both optional parameters so TypeScript doesn't complain when none of them are provided.
Possible solution
I was thinking about something like this:
type databaseNameOrId = ({ databaseId: string } | { databaseName: string });
export interface Userbase {
insertItem(
params: databaseNameOrId & { item: any; itemId?: string }
): Promise<void>;
}
Personally, I think this syntax is acceptable. However, the type-error that TypeScript generates when none of both parameters are provided is somewhat noisy & possibly unclear.
Argument of type '{ item: string; }' is not assignable to parameter of type '({ databaseId: string; } & { item: any; itemId?: string; }) | ({ databaseName: string; } & { item: any; itemId?: string; })'.
Type '{ item: string; }' is not assignable to type '{ databaseName: string; } & { item: any; itemId?: string; }'.
Property 'databaseName' is missing in type '{ item: string; }' but required in type '{ databaseName: string; }'.
I've made an example repository here where you can try it for yourselves: https://github.com/mhxbe/userbase-database-types
I hope there's a better way of typing this, but I'm not sure.
Suggestions/improvements are welcome.
curl 'https://v1.userbase.com/v1/admin/databases/$DATABASE_ID/users?nextPageToken=$NEXT_PAGE_TOKEN' \
-XGET \
-H 'Authorization: Bearer $ACCESS_TOKEN'
See here for the other endpoints.
AssertionError: expected '' to have a property 'userbase'
Because this error occurred during a 'before each' hook we are skipping the remaining tests in the current suite: 'Signup Testing'
As proposed by @alexcnichols in #59, in addition to Userbase's current forgot password mechanism (#103), it would be a nice feature to allow users who lose their password to delete all their data and create a new password.
resetAccount({ username })
or something similar, which sends a message to the user's email.signIn()
.signIn()
, Userbase will mark all the user's old data for deletion (to be deleted asynchronously in the nightly purge) then allow the user to sign in normally (& create a new password).Note, the user's plaintext data (profile + protectedProfile) would not be deleted.
On every delete operation on items from the to-do example the password manager is triggered (tested 1password on mac on my laptop). A prompt is shown to store the password.
Right now the changeHandler receives just one argument, which is the entire list of items in a database. I would like to receive information on exactly which items were changed so I don't have to iterate over them myself to figure it out. This could be a second argument passed to the changeHandler.
Hi, not really an issue but a question.
I want to build an app where a coach can write progress reports for his clients, and I am absolutely not sure how to model this.
In #142 i've read that it is totally ok, to have more than one database.
Does this mean, in my case, i can create two databases - one for the clients, one for the progress reports - and connect them somehow?
As far as i understand the insertItem documentation, it's possible to specify only one database at a time. So my Workflow would be, to have some kind of a details page for every client where i can create the progress reports?
Thanks in advance for your help!
In addition to the current two flags, readOnly
and resharingAllowed
, I've found myself wanting a third option, where an invited user could insert new documents, but could only update or delete documents that they themselves inserted.
This might be a new property like insertOnly: true
or it might be some non-boolean value for readOnly
, like { "readOnly": "others" }
1. Opening database removes existing handler
I guess this behavior is intentional, but it's not obvious. I'd be happy to document it.
2. changeHandler
is called on database open
It's also not obvious that changeHandler
would behave this way, and it contradicts the documentation:
A callback that gets invoked when the database gets modified.
A more natural behavior might be to skip the initial call of changeHandler
and instead resolve openDatabase
with the data:
const initial = await userbase.openDatabase({
databaseName: '',
changeHandler() {},
});
3. Unable to unsubscribe from changeHandler
or close database
A wee memory leak. And it's a bit cumbersome for the user, who has maintain some state to key off of in changeHandler
to determine whether or not to process the change.
4. Nit: Options are unnecessarily verbose
databaseName
-> name
changeHandler
-> onChange
It doesn't look like there's an SDK method for deleting a database—only one for deleting individual items in batches of ten.
I'm building an RSS reader and using Userbase to sync feed subscriptions and unread articles. I thought I'd create a feeds
database and store each feed as an item in it. Like so:
userbase.insertItem({
databaseName: 'feeds',
item: {
url: feed.url,
unread: [{
date: article.date,
url: article.url,
}],
},
itemId: md5(feed.url), // MD5: 100 character limit
})
However, it's not uncommon for a user to let unread articles pile up, leading to tens or hundreds of unread. It makes sense to limit unread articles somewhat—perhaps to the most recent hundred. But the 10 KB limit on a databases items prevents me from storing even 50 unread
.
So I'm instead creating a database per feed, while storing each unread
as an individual item. I never run up against the 10 KB limit for items, of course. But it's cumbersome to clear out a feed database in batches of ten (rate-limited, of course) after a user has unsubscribed from a feed. I'd like to simply delete the database and be done.
I also wonder. Is creating a database per feed even advisable? It's the only way I can see an RSS reader existing given the 10 KB item limit. But is there a hard limit on databases per user? Because I can easily imagine users who are subscribed to a couple hundred feeds. If there is a limit, some error messaging would be helpful.
Anywho, thanks for making Userbase.
Steps
init
Expected
init
rejects.
Actual
init
never settles. Handling XHR's onerror
should fix the issue.
If somebody has access your server and modifies the client's javascript, can steal the users key.
I don't know if this is intentional, but when I delete a user in the admin interface who previously had a shared database, getDatabases
throws an exception when logged in as the database owner:
Uncaught (in promise) ServiceUnavailable: Service unavailable.
at _parseGenericErrors (webpack-internal:///./node_modules/userbase-js/lib/db.js:49:13)
at Object.getDatabases (webpack-internal:///./node_modules/userbase-js/lib/db.js:1660:7)
Note: This happens as soon as I permanently delete a user in the interface. It still works if the deletion is pending.
These two behave slightly different.
updateHandler
gets items as the argument, while updateUserHandler
gets an object (with user as the attribute) as the argument. Might be messing up my terms here, but it would therefore be beneficial with an example also on the init documentation page similar to the one for openDatabase:
This exists:
changeHandler: function (items) {
// update your application state with the database items
}
This should be added:
updateUserHandler: function ({user}) {
// update your application state with the updated user
}
So one does not have to manage updating the user data in the client code. It could also be changed on another client and would be nice to get the current user from the server whenever doing change,
As part of the early preview, the primary reservation I've had is around the demand put on end users to retain the decryption key.
I was happy to see this week in the FAQ that the method of storing the key was enhanced to generate based on the user's password and store on the Userbase servers. That solves some complexity and removes the dependency to be locked into a single browser's storage... from what I can tell, this means users can roam between between browsers and devices and still login.
The demand on the end user to never lose their password remains though.
At the very least I could imagine a feature to "Delete all data and create new password". 🤷♂
Great work so far! Looking forward to the upcoming public launch. 🚀 🎉
The lines: https://github.com/encrypted-dev/userbase/blob/master/src/userbase-server/setup.js#L378
are ignoring any kind of proper setup of the region as per https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-region.html
Even if running on EC2 we should allow the users to pick another region as per their config.
While signing up, after pressing the signup button, it did not move to the login page or logged me in.
Instead it kept showing the loader, i waited for 30 seconds.
But, the sign up was successful.
(Ubuntu, Chrome).
Also I did not get the key.
I love it when I can see releases of other projects as github releases.
No need to document all, but at least major versions.
A project doing this well is https://github.com/mui-org/material-ui/releases
Hello,
I scanned your project with SonarCloud and I realized there are 2 categories of bugs detected that you should look at:
The problem is located on src/userbase-server/app.js
If !app
is true
, it means app
is undefined so app['app-id']
is also undefined and will fail at runtime.
Regards
Alex
I've been playing with Userbase over the last few days and I have a few doubts regarding how large amounts of data is handled.
Suppose a user's database has over 100mb data. As far as I'm aware, the changeHandler picks up all items available in the database. Does this mean all 100mb will get downloaded on the client side at some point?
If that is the case, it would be nice to have pagination support. It could be a new endpoint called getDatabasePage with parameters "pageSize", "pageNumber" and "databaseName".
openDatabase can have a parameter "doNotFetch" to avoid downloading all that data.
A few alternative approaches
These are some approaches I've thought of that wouldn't require a new endpoint but will need some client side coding to manage.
(1) Pagination by date ranges
A cheeky alternative to this would be to create a new database every week / month per user depending on the amount of data the app is going to generate and to fetch those databases on-demand. Pagination by date ranges essentially.
(2) Pagination by total items
Create a new database for every X items, depending on item size and keep a database counter in the user's profile.
Please let me know if my understanding of Userbase is wrong or if I'm missing something important.
Thanks!
Hi,
Please list in README what exact AWS resources are being used and what for.
This is also useful to get an estimate of AWS billing.
Thanks
Proposed usage:
userbase.itemHistory({ databaseName, itemId }).then(revisions => {
for (const revision of revisions) {
const { item, username, userDeleted, timestamp } = revision
// maybe file stuff too? Not sure if old files are stored permanently
}
})
Maybe would always be fetched from transaction log rather than the bundle.
To reproduce:
I would like to capture and save some data from the user before sending them to payment.
It could be a setting in admin that lets us add database names that should not be paywalled.
Currently, according to the docs, any database of a user is deleted as soon as he deletes his account. This also happens with shared databases. Would it be possible to implement a functionality to change the owner of a database to prevent this?
Odd error. But signing the user in and unmarking his account as pending deletion might be a better behavior.
Thank you for making this OSS. I've been reading the code and it's a great example of how to create a multi tenant SaaS product. I wanted to understand for my own purposes if this could be deployed in a self-hosted manner with more of a serverless / pay-per-use approach. My thought was API Gateway websockets + lambda. I understand why it may not work for your hosted offering you run due to limits/quotas/latency, but assuming those are not a concern/blocker for self-hosting, are there any technical challenges to go with this approach? Long-running process(es) (lambda limits - time, memory), preserving e2e encryption, some other security aspect, etc.?
If not, I'd look to build out and contribute back here.
Thanks in advance for any help!
My user's have been asking me why they have to log in again all the time.
They want it to be more "app like" and are not to worried about someone accessing their phones.
Could it be possible to get a remember me until I explicitly sign out.
Or is this already possible and I have just missed the docs.
At this time, any individual can copy the app id from the application code and use that app id. Although the app id can't be used to access user information, someone could still use that app id to create and deploy a web app that uses it.
Have the ability to list domains the app id can be used on. I don't believe this is a major security concern and needs addressed right now, but I do think it would be good at some point to put in place as another item to check off with regards to better security/best practices.
Thank you Userbase for all the hard work you do! And for this awesome service!
When using the JavaScript SDK, calling userbase.getDatabases()
seems to send a "GetDatabaseUsers" message through the websocket for every single database that the user has access to. Because they are sent all at once, this trips the websocket server's rate limit of 25 messages per second. The result is a confusing ServiceUnavailable
error with a TooManyRequests
error inside.
Wow, At first I wasn't for sure if I understood correctly what userbase was... until I started reading the DOCS and the code examples and now I am very excited to say:
I THINK YOU GUYS MIGHT HAVE SOLVED A MULTI LOGIN FOR WORDPRESS!
Now I know this app is made for static sites specifically, but I just had to offer a suggestion and request.
Is it possible that if this is tied to a Wordpress site as the main sign up, when I user signs up in Wordpress they will see a userbase signup screen... After signing up, userbase pushes to that Wordpress site's USERS WP REST API endpoint to update the users of a family of WP sites user database tables?
For example, To create a User in WP:
https://developer.wordpress.org/rest-api/reference/users/#create-a-user
It respectfully UPDATES, DELETES users across multi spectrums using userbase as the Master commando!
https://developer.wordpress.org/rest-api/reference/users/#update-a-user
https://developer.wordpress.org/rest-api/reference/users/#delete-a-user
This seems like a simple feat but will be powerful!!!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.