Last active
January 22, 2022 21:44
-
-
Save romshark/cdcf20f5e6ebbe1265f121b7ad133705 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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