Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.

Commit 1c8a3a6

Browse files
committed
bug fixes with ssf and message queue and updated README
1 parent 6b257f1 commit 1c8a3a6

File tree

8 files changed

+97
-55
lines changed

8 files changed

+97
-55
lines changed

README.md

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,71 +40,78 @@ over and over on the backend. If your application needs one or all of
4040
user management, database, file storage, real-time interactions, it should be
4141
a good fit.
4242

43-
I'm personally using it to build SaaS.
43+
I'm personally using it to build SaaS:
4444

45-
## How it works / dev workflow
46-
47-
The main idea is that StaticBackend is your backend API for your frontend apps.
48-
49-
It needs to have access to MongoDB and Redis servers. Once you have your instance
50-
running you create accounts for your applications.
45+
* [Tangara - one page checkout for creators](https://tangara.io)
5146

52-
An account has its own database, file storage, etc.
53-
54-
I think `app` might have been a better name instead of `account`. Naming things
55-
is hard.
47+
## How it works / dev workflow
5648

57-
A StaticBackend account(app) can have multiple user accounts and each user
58-
accounts may have multiple users.
49+
The main idea is that StaticBackend is your backend API for your frontend apps.
50+
A performant free and open-source Firebase alternative.
5951

60-
From there each users can create database documents that are by default Read/Write
61-
for the owner (the user) and Read for its parent account. You may customize
62-
permission for each of your collection (see that later in the documentation).
52+
_Note that it can also be used from your backend code as well._
6353

64-
You have the basic building blocks to create a typical web
65-
application. You have all your CRUD and data query operations covered, file
66-
storage and websocket-like capabilities.
54+
Once you have an instance running and your first app created, you may install
55+
the JavaScript client-side library:
6756

68-
We have a [JavaScript library](https://www.npmjs.com/package/@staticbackend/js) to
69-
get started quickly. We have also server-side libraries for Node and Go atm.
57+
```shell
58+
$> npm install @staticbackend/js
59+
```
7060

71-
Why would you need server-side libraries, was it not supposed to be a backend
72-
for client-side applications.
61+
Let's create a user account and get a session `token` and create a `task`
62+
document in the `tasks` collection:
7363

74-
Yes, but, there's always a but. Sometimes your application will need to
75-
perform tasks on behalf of users or public user that do not have access to
76-
perform CRUD from the client-side.
64+
```javascript
65+
import { Backend } from "@staticbackend/js";
7766

78-
Let's imagine we're building an invoicing app. Here's the major entities
79-
we have for this examples:
67+
const bkn = new Backend("your_public-key", "dev");
8068

81-
* A StaticBackend account (our app inside our SB instance)
82-
* An account with 2 users (this would be your customer)
83-
* An invoices collection (where your customer create invoice)
84-
* A clients collection (Your customer send invoice to their clients)
69+
let token = "";
8570

86-
Now let's imagine our customer (our app user) sends an invoice to their Client.
71+
login = async () => {
72+
const res = await bkn.login("[email protected]", "password");
73+
if (!res.ok) {
74+
console.error(res.content);
75+
return;
76+
}
77+
token = res.content();
8778

88-
Their client does not have any user account, but they need to see their invoice
89-
when they click on the unique link on their email they received.
79+
createTask();
80+
}
9081

91-
This can be achieve via a backend function. Couple of ways:
82+
createTask = async () => {
83+
const task = {
84+
desc: "Do something for XYZ",
85+
done: false
86+
};
9287

93-
* The email the client received can be directly a unique URL pointing to a
94-
function as a service hosted somewhere. (We will have functions soon). That
95-
function returns the invoice HTML.
96-
* Or it could be pointing to your client-side app and you perform a call to
97-
a serverless function you're hosting somewhere. That function uses the
98-
server-side library to perform a `GET` on behalf of the creator of the invoice
99-
document, which is our customer.
88+
const res = bkn.create(token, "tasks", task);
89+
if (!res.ok) {
90+
console.error(res.content);
91+
return;
92+
}
93+
console.log(res.content);
94+
}
95+
```
10096

101-
The function will be able to perform a Read operation using a special `Root Token`.
97+
The last `console.log` prints
10298

103-
This `Root Token` allows your system to do anything on the server-side.
99+
```json
100+
{
101+
"id": "123456-unique-id",
102+
"accountId": "aaa-bbb-unique-account-id",
103+
"desc": "Do something for XYZ",
104+
"done": false
105+
}
106+
```
104107

105-
I hope I did not lost the majority of people here ;)
108+
From there you build your application using the
109+
[database](https://staticbackend.com/docs/database/) CRUD and query functions,
110+
the [real-time component](https://staticbackend.com/docs/websocket/),
111+
the [storage API](https://staticbackend.com/docs/storage/), etc.
106112

107-
This is one example of your typical day-to-day workflow using StaticBackend.
113+
You may use server-side libraries for Node and Go or use an HTTP client
114+
and use your preferred language.
108115

109116
## Get started with the self-hosted version
110117

@@ -195,6 +202,7 @@ Here's the examples we have created so far:
195202

196203
* [To-do list example](https://staticbackend.com/getting-started/)
197204
* [Realtime collaboration](https://staticbackend.com/blog/realtime-collaboration-example/)
205+
* [Live chat using server-side function & real-time component](https://staticbackend.com/blog/server-side-functions-task-scheduler-example/)
198206

199207
## Deploying in production
200208

function/runtime.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ func (env *ExecutionEnvironment) addVolatileFunctions(vm *goja.Runtime) {
328328
if err != nil {
329329
return vm.ToValue(Result{Content: fmt.Sprintf("error converting your data: %v", err)})
330330
}
331+
fmt.Println("DEBUG: ", string(b))
331332

332333
msg := internal.Command{
333334
SID: env.Data.ID.Hex(),

function/subscriber.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ func (sub *Subscriber) Start() {
2626
select {
2727
case msg := <-receiver:
2828
go sub.process(msg)
29-
case _ = <-close:
29+
case <-close:
3030
log.Println("system event channel closed?!?")
31-
break
3231
}
3332
}
3433
}

functions.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package staticbackend
22

33
import (
4-
"fmt"
54
"net/http"
65
"staticbackend/db"
76
"staticbackend/function"
@@ -100,9 +99,6 @@ func (f *functions) exec(w http.ResponseWriter, r *http.Request) {
10099

101100
curDB := client.Database(conf.Name)
102101

103-
fmt.Println("DEBUG: search for name: ", data.FunctionName)
104-
fmt.Println("DEBUG: base: ", conf.Name)
105-
106102
fn, err := function.GetForExecution(curDB, data.FunctionName)
107103
if err != nil {
108104
http.Error(w, err.Error(), http.StatusInternalServerError)

internal/account.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package internal
33
import (
44
"context"
55
"fmt"
6+
"strings"
67
"time"
78

89
"github.com/gbrlsnchs/jwt/v3"
@@ -21,6 +22,9 @@ type Auth struct {
2122
}
2223

2324
func (auth Auth) ReconstructToken() string {
25+
if strings.HasPrefix(auth.Token, "__tmp__experimental_public") {
26+
return auth.Token
27+
}
2428
return fmt.Sprintf("%s|%s", auth.UserID.Hex(), auth.Token)
2529
}
2630

internal/mailer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type SendMailData struct {
1515
HTMLBody string `json:"htmlBody"`
1616
TextBody string `json:"textBody"`
1717
ReplyTo string `json:"replyTo"`
18+
19+
Body string `json:"body"`
1820
}
1921

2022
// Mailer is used to have different implementation for sending email

sendmail.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package staticbackend
33
import (
44
"context"
55
"net/http"
6+
"staticbackend/email"
67
"staticbackend/internal"
78
"staticbackend/middleware"
89

@@ -16,6 +17,25 @@ func sudoSendMail(w http.ResponseWriter, r *http.Request) {
1617
return
1718
}
1819

20+
// all email are sent from the env var MAIL_FROM
21+
from := data.From
22+
if len(data.ReplyTo) > 0 {
23+
from = data.ReplyTo
24+
}
25+
26+
data.From = FromEmail
27+
data.ReplyTo = from
28+
29+
// if only body is provided
30+
if len(data.Body) > 0 {
31+
data.HTMLBody = data.Body
32+
data.TextBody = email.StripHTML(data.Body)
33+
} else if len(data.TextBody) == 0 && len(data.HTMLBody) > 0 {
34+
data.TextBody = email.StripHTML(data.HTMLBody)
35+
} else if len(data.HTMLBody) == 0 && len(data.TextBody) > 0 {
36+
data.HTMLBody = data.TextBody
37+
}
38+
1939
if err := emailer.Send(data); err != nil {
2040
http.Error(w, err.Error(), http.StatusInternalServerError)
2141
return

server.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,20 @@ func Start(dbHost, port string) {
5858
b := realtime.NewBroker(func(ctx context.Context, key string) (string, error) {
5959
//TODO: Experimental, let un-authenticated user connect
6060
// useful for an Intercom-like SaaS I'm building.
61-
if strings.HasPrefix(key, "__tmp__experimental_public_19378246_") {
61+
if strings.HasPrefix(key, "__tmp__experimental_public") {
6262
// let's create the most minimal authentication possible
6363
a := internal.Auth{
6464
AccountID: primitive.NewObjectID(),
6565
UserID: primitive.NewObjectID(),
6666
6767
Role: 0,
68+
Token: key,
6869
}
6970

7071
if err := volatile.SetTyped(key, a); err != nil {
7172
return key, err
7273
}
74+
7375
return key, nil
7476
}
7577

@@ -232,7 +234,16 @@ func initServices(dbHost string) {
232234
var exe function.ExecutionEnvironment
233235

234236
var conf internal.BaseConfig
235-
if err := volatile.GetTyped("base:"+token, &conf); err != nil {
237+
// for public websocket (experimental)
238+
if strings.HasPrefix(token, "__tmp__experimental_public") {
239+
pk := strings.Replace(token, "__tmp__experimental_public_", "", -1)
240+
pairs := strings.Split(pk, "_")
241+
fmt.Println("checking for base in cache: ", pairs[0])
242+
if err := volatile.GetTyped(pairs[0], &conf); err != nil {
243+
log.Println("cannot find base for public websocket")
244+
return exe, err
245+
}
246+
} else if err := volatile.GetTyped("base:"+token, &conf); err != nil {
236247
log.Println("cannot find base")
237248
return exe, err
238249
}
@@ -246,6 +257,7 @@ func initServices(dbHost string) {
246257
exe.Auth = auth
247258
exe.Base = &db.Base{PublishDocument: volatile.PublishDocument}
248259
exe.DB = client.Database(conf.Name)
260+
exe.Volatile = volatile
249261

250262
return exe, nil
251263
}

0 commit comments

Comments
 (0)