Skip to content

Instantly share code, notes, and snippets.

@romshark
Last active January 22, 2022 21:44
Show Gist options
  • Save romshark/cdcf20f5e6ebbe1265f121b7ad133705 to your computer and use it in GitHub Desktop.
Save romshark/cdcf20f5e6ebbe1265f121b7ad133705 to your computer and use it in GitHub Desktop.
package blog
import "std" 1.0 { bytelen }
import "session" 1.0 { field as sessionField }
import "time" 1.0 { Time, now }
import "sys" 1.0 { env }
import "json" 1.0 { JSONValue }
import "github.com/sml/x/jwt" 1.0 {
parsed as jwtParsed
signed as jwtSigned
SigningMethod as JWTSigningMethod
SigningAlg as JWTSigningAlg
}
import "github.com/sml/x/ksuid" 1.3 { new as ksuid }
import "github.com/sml/x/bcrypt" 2.0 {
bcryptCorresponds
bcryptHash
}
service resolves {
posts []Post => Post[sorting: Post.publication, sortAsc: false]
mutNewUser User|Error(String name, email, password) => match {
name == "" or len(name) > 256 = Error{"invalid name"}
email == "" or len(email) > 256 = Error{"invalid email"}
password == "" or len(password) > 256 = Error{"invalid password"}
default = new(User{
id: newID(),
registration: now(),
name: name,
email: email,
password: bcryptHash(password, 10),
})
}
mutNewPost Post|Error(String title, body) => authUser(Post|Error => {
title == "" or len(title) > 256 = Error{"invalid title"}
body == "" or bytelen(body) > 65_536 = Error{"invalid body"}
default = new(Post{
id: newID(),
publication: now(),
title: title,
body: body,
})
)
mutNewComment Comment|Error(
ID subject,
String body,
) => authUser(Comment|Error => {
match {
errBody == Error = errBody
len(p) > 0 = {
new(nc)
new(Post{
comments: concat(p.comments, nc),
..p,
})
var nc = Comment{
id: newID(),
body: body,
publication: now(),
author: author,
subject: p,
}
var p = p[0]
}
len(c) > 0 = {
new(nc)
new(Post{
comments: concat(c.comments, nc),
..c,
})
var nc = Comment{
id: newID(),
body: body,
publication: now(),
author: author,
subject: c,
}
var c = c[0]
}
default = Error{"subject not found"}
}
var errBody = errBody(body)
var c = Comment[
predicate: Comment.id == subject,
limit: 1,
]
var p = Post[
predicate: Post.id == subject,
limit: 1,
]
})
mutCommentRevision Comment|Error(
ID id,
String body,
) => {
match {
errBody == Error = errBody
len(c) < 1 = Error{"comment not found"}
default = {
authOwner(c[0].author, Comment => new(Comment{
body: body,
..c[0],
}))
}
}
var errBody = errBody(body)
var c = Comment[
predicate: Comment.id == id,
limit: 1,
]
}
mutNewAccessToken String|ErrWrongCreds (String email, password) => {
match {
len(u) < 1 or !bcryptCorresponds(u.password, password) =
ErrWrongCreds{"wrong email or password"}
default = jwtSigned(JWTSigningAlg.HS256, JWTPayload{uid: userID})
}
var u = User[
predicate: User.email == email,
limit: 1,
]
}
}
type Error String
type Post entity {
String id
String title
String body
User author
Time publication
Comment->Nil comments
} resolves as c {
id ID => c.id
title String => c.title
body String => c.body
publication Time => c.publication
author User => c.author
comments []Comment => sort(
keys(u.comments),
(Comment i, j) => i.publication > j.publication,
)
}
type Comment entity {
ID id
CommentSubject subject
String body
Time publication
User author
Comment->Nil comments
} resolves as c {
id ID => c.id
subject CommentSubject => c.subject
body String => c.body
publication Time => c.publication
author User => c.author
comments []Comment => sort(
keys(u.comments),
(Comment i, j) => i.publication > j.publication,
)
}
type CommentSubject Post|Comment
type User entity {
ID id
String name
String email
PasswordHash password
Time registration
Post->Nil posts
} resolves as u {
id ID => u.id
name String => u.name
email String => authOwner(u, String => u.email)
posts []Post => sort(
keys(u.posts),
(Post i, j) => i.publication > j.publication,
)
}
// authOwner is ErrUnauth if the client performing the request != owner
func authOwner ?Data|ErrUnauth (Author owner, ?Data() data) => match {
owner.id == clientUserID() = data()
default = ErrUnauth
}
// authUser is ErrUnauth if the client performing the request
// is an unauthenticated guest
func authUser ?Data|ErrUnauth (?Data() data) => match {
clientUserID() == ID = data()
default = ErrUnauth
}
type ErrUnauth Error
type ID String
func errBody Error|Nil(String body) => match {
body == "" or bytelen(body) > 65_536 then Error{"invalid body"}
}
func newID ID() => ksuid()
// clientUserID is the ID of the client performing the request
func clientUserID ID|Error => jwtParsed(
sessionField("token"), // JWT
JWTSigningMethod.HMAC, // expected signing method
env("JWT_SECRET"),
ID|Error (JSONValue v) => match v as v {
String = ID{v}
default = Error{"malformed payload"}
},
)
type PasswordHash String
type ErrWrongCreds Error
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment