standardfile
standardfile copied to clipboard
Subscriptions
The developers of StandardNotes have provided an official way to enable the 'Pro' subscription features on self-hosted instances:
https://docs.standardnotes.com/self-hosting/subscriptions/
Is it possible to build this into your server?
It may be implemented with a fake API path returning the expected "Pro plan" payload to the frontend rather that implementing the whole subscription feature which has no meaning for this server. But it will take hours of work to analyze and support this feature only for three "Note Types" which I don't really need. (An nginx in front of the server should also be able to return the expected payload)
There is also this statement in the doc to consider:
Building Standard Notes has high costs. If everyone evaded contributing financially, we would no longer be here to continue to build upon and improve these services for you. Please consider donating if you do not plan on purchasing a subscription.
For the moment I will try to fix the refresh session issue and other issues.
PS: This project is opened to contributions.
I looked into it. The StandardNotes app tries to retrieve the subscription data from v2/subscriptions
. This is a valid response:
{
"meta": {
"auth": {
"userUuid": "01170b7c-6b5c-480d-b3dd-2a619195fc56",
"roles": [
{
"uuid": "e3760e5a-7d10-417a-aa68-df3afa3a0dab",
"name": "PRO_USER"
},
{
"uuid": "6468d154-36aa-453d-baf5-708d221351c3",
"name": "BASIC_USER"
}
]
}
},
"data": {
"success": true,
"user": {
"uuid": "01170b7c-6b5c-480d-b3dd-2a619195fc56",
"email": "[email protected]"
},
"subscription": {
"uuid": "e7f7e5db-82ec-4a1f-a827-62191fc7d564",
"planName": "PRO_PLAN",
"endsAt": 8640000000000000,
"createdAt": 0,
"updatedAt": 0,
"cancelled": 0,
"subscriptionId": 1
}
}
}
Apart from the userUuid, each 'role' has a uuid (I assume on a per-user basis) and each subscription has a uuid, I assume a per user one, as it's only the name that is being checked.
I think BASIC_USER
is the same as a 'free' user, so basically every user at least has this tier.
A user who has only the BASIC_USER
tier returns data
without a subscription (only success
and user
in that case).
It also tries to access v1/users/{uuid}/subscription
, which returns:
subscriptions getaddrinfo EAI_AGAIN payments
The latter one seems like something only used on the hosted instance for payment info, which is not applicable to self-hosted users. The functions responsible for these requests can be found here.
So to implement it, it would take to:
- Either have a database table for per-user roles (and a table for plans) or a environment variable to set the
PLAN_STATUS
- In case of the former, have a way for a user to set it
- Implement an endpoint for
v2/subscriptions
, which returns the required info - Optionally implement a dummy endpoint for
v1/users/{uuid}/subscription
I think the environment variable is the most easy option, while the custom subscription enabler (maybe with a reference that kindly asks to donate something to Standard Notes) being the most 'ethical' way.
Very interesting, thanks for looking into this @SerialDestructor !
My Standard File server is running behind a caddy reverse proxy, and it could be set up to serve static content like a JSON file at v2/subscriptions if necessary. However, it seems that I would need to know the UUID of every user and also I'm concerned about exposing the username since it's part of the answer in plain text. Or is this encrypted somehow?
Can any of the options you enumerate be implemented by end-users like me without adding code to this project? I wish I knew how to code :-)
However, it seems that I would need to know the UUID of every user and also I'm concerned about exposing the username since it's part of the answer in plain text. Or is this encrypted somehow?
No, the response is different for each user account. With a static JSON file, the email address of the notes account is there in plain text, for everyone to see who knows the endpoint.
Can any of the options you enumerate be implemented by end-users like me without adding code to this project? I wish I knew how to code :-)
Well, as you mentioned, it would be possible to do it hard coded for one account and it could work, but again, your account email address and uuid will be exposed. It would stay away from that option.
I have coding expierence, but not in GoLang. I could give it a try nevertheless, but there are a lot of project-specific things (such as restricted vs non-restricted endpoints, which both seem only accessible after a login) which I don't know the difference about. Some insights about where to look would certainly help.
"restricted" routes need authentication (Authorization
header in request).
I tried to dig in this feature, using the standalone docker-compose and app.standardnotes.org:
-
Retrieve user's subscription
GET /v1/users/dd07dcba-3884-4dbd-9cf4-960fbb57bd99/subscription 200 OK { "meta": { "auth": { "userUuid": "dd07dcba-3884-4dbd-9cf4-960fbb57bd99", "roles": [ { "uuid": "8047edbb-a10a-4ff8-8d53-c2cae600a8e8", "name": "PRO_USER" }, { "uuid": "8802d6a3-b97c-4b25-968a-8fb21c65c3a1", "name": "BASIC_USER" } ] } }, "data": { "success": true, "user": { "uuid": "dd07dcba-3884-4dbd-9cf4-960fbb57bd99", "email": "[email protected]" }, "subscription": { "uuid": "caf81bfc-bfcf-11ec-b43b-0242ac120008", "planName": "PRO_PLAN", "endsAt": 8640000000000000, "createdAt": 0, "updatedAt": 0, "cancelled": 0, "subscriptionId": 1 } } }
-
Check payment? There is no
Authorization
header from the request so the route seems not authenticatedGET /v2/subscriptions 500 Internal Server Error getaddrinfo ENOTFOUND payments
-
Load the user's features
GET /v1/users/dd07dcba-3884-4dbd-9cf4-960fbb57bd99/features 200 OK { Large JSON payload of all features and their URLs }
I tried to fake GET /v1/users/:id/subscription
with data.user.email/data.user.uuid/meta.auth.userUuid updated and GET /v2/subscriptions
with the same 500 Internal Server Error payload but it does not work, GET /v1/users/:id/features
is never requested.
I don't know what I'm missing here.
I was already experimenting with this, but since I don't know Go to well and needed to do some database changes (not familiar with BoltDB either), it was taking up too much time for the time being.
@mdouchement could you share what you tried? I will see if I can look into that as soon as I have some time.
Yeah sure, here my sandbox https://github.com/mdouchement/standardfile/compare/subscription-sandbox?expand=1
I can't pretend I haven't been checking this thread daily, hoping for an update from you :-)
Maybe this will help understand how it works? https://github.com/standardnotes/standalone/issues/64#issuecomment-1064719310
With standardnotes you only have to add these to the database to unlock the pro feature.
INSERT INTO user_roles (role_uuid , user_uuid) VALUES ( ( select uuid from roles where name="PRO_USER" order by version desc limit 1 ) ,( select uuid from users where email="<EMAIL@ADDR>" ) ) ON DUPLICATE KEY UPDATE role_uuid = VALUES(`role_uuid`);
insert into user_subscriptions set uuid = UUID() , plan_name="PRO_PLAN" , ends_at = 8640000000000000, created_at = 0 , updated_at = 0,user_uuid= (select uuid from users where email="<EMAIL@ADDR>") , subscription_id=1;