Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F10360772
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/README.md b/README.md
index 7e8e7fb4..0cc0700a 100644
--- a/README.md
+++ b/README.md
@@ -1,139 +1,143 @@
[](http://sealious.github.io/)
# Sealious
Sealious is a declarative node.js framework. It creates a full-featured REST-ful
API (with user and session management) based on a declarative description of the
database schema and policies.
You can use it to create a full-featured REST API and ORM with minimal amount of code.
## Example
Instal sealious with `npm install --save sealious`. Then, in your index.ts:
```
lang=typescript
import { resolve } from "path";
import Sealious, { App, Collection, FieldTypes, Policies } from "sealious";
const locreq = _locreq(__dirname);
const app = new (class extends App {
config = {
datastore_mongo: {
host: "localhost",
port: 20723,
db_name: "sealious-playground",
},
upload_path: locreq.resolve("uploaded_files"),
email: {
from_address: "sealious-playground@example.com",
from_name: "Sealious playground app",
},
"www-server": {
port: 8080, //listen on this port
},
};
manifest = {
name: "My ToDo list",
logo: resolve(__dirname, "../assets/logo.png"),
version: "0.0.1",
default_language: "en",
base_url: "localhost:8080",
admin_email: "admin@example.com",
colors: {
primary: "#5294a1",
},
};
collections = {
...App.BaseCollections,
tasks: new (class extends Collection {
fields = {
title: new FieldTypes.Text(),
done: new FieldTypes.Boolean(),
};
defaultPolicy = new Policies.Public();
})(),
};
})();
app.start();
```
Assuming you have the mongo database running, that's it! The above script
creates a fully functional REST API with field validation, error messages, etc.
Try sending as POST message to `http://localhost:8080/api/v1/collections/tasks`
to see the API in action. You can learn more about the endpoints created by
Sealious for each collection [in ./endpoints.remarkup doc
file](https://hub.sealcode.org/source/sealious/browse/dev/endpoints.remarkup).
The app created by the above code also has some handy ORM-style methods to access and modify items within the collection:
```
lang=typescript
import {Context} from "sealious";
const tasks = app.collections.tasks.list(new Context(app)).fetch()
```
To learn more about the ORM methods, see [./orm.remarkup doc file](https://hub.sealcode.org/source/sealious/browse/dev/orm.remarkup).
## Learning Resources
### Examples
It's best to learn by example. Here are some applications written with the
current version of Sealious:
- [Sealious Playground](https://hub.sealcode.org/diffusion/PLAY/) - simple
TODO app written in Sealious and Hotwire. Contains docker setup for mongo,
linting, typescript etc. Good starting point for a new app.
### References
-- [List of all endpoints automatically created by Sealious](https://hub.sealcode.org/source/sealious/browse/dev/endpoints.remarkup)
+- [List of all endpoints automatically created by
+ Sealious](https://hub.sealcode.org/source/sealious/browse/dev/endpoints.remarkup)
- [ORM style accessors to database](https://hub.sealcode.org/source/sealious/browse/dev/orm.remarkup)
- [Theory and practice behind Context](https://hub.sealcode.org/source/sealious/browse/dev/context.remarkup)
- [List of Built-in field
types](https://hub.sealcode.org/source/sealious/browse/dev/src/app/base-chips/field-types/field-types.remarkup)
-- [Creating custom field-types](https://hub.sealcode.org/source/sealious/browse/dev/src/app/base-chips/field-types/creating-field-types.remarkup)
+- [Creating custom
+ field-types](https://hub.sealcode.org/source/sealious/browse/dev/src/app/base-chips/field-types/creating-field-types.remarkup)
- [List of build-in Policies](https://hub.sealcode.org/source/sealious/browse/dev/src/app/policy-types/policy-types.remarkup)
+- [Creating custom Policy
+ types](https://hub.sealcode.org/source/sealious/browse/dev/src/app/policy-types/creating-policy-types.remarkup)
### FAQ
#### How do I add a custom route?
Sealious uses `koa` and [@koa/router](https://github.com/koajs/router) to handle HTTP. To add a simple static route:
```
lang=typescript
app.HTTPServer.router.get("/", async (ctx) => {
ctx.body = html(/* HTML */ `
<body>
<h1>Hello, world!</h1>
</body>
`);
});
```
If you need to perform some user-specific tasks, or need to extract the context in order to call the database, use the `extractContext` Middleware:
```
lang=typescript
import {Middlewares} from "sealious";
app.HTTPServer.router.get("/", Middlewares.extractContext(), async (ctx) => {
const tasks = await app.collections.tasks.list(ctx.$context).fetch();
ctx.body = html(/* HTML */ `
<body>
<h1>My To do list</h1>
{tasks.map(task=>task.get("title")).join("")}
</body>
`);
});
```
## Technical docs
For technical reference, see
[sealious.sealcode.org/docs](https://sealious.sealcode.org/docs)
diff --git a/src/app/policy-types/creating-policy-types.remarkup b/src/app/policy-types/creating-policy-types.remarkup
new file mode 100644
index 00000000..9f5db2f3
--- /dev/null
+++ b/src/app/policy-types/creating-policy-types.remarkup
@@ -0,0 +1,70 @@
+# Creating custom policy types
+
+When the [built-in policy
+types](https://hub.sealcode.org/source/sealious/browse/dev/src/app/policy-types/policy-types.remarkup)
+are not enough, you can build your custom policy type and use it in a way
+identical to as you'd use the built-in ones.
+
+As policies rely heavily on [MongoDB Aggregation Pipeline
+Stages](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/),
+it's recommended to have a basic understanding of how the aggregation pipeline
+works. [This tutorial from MongoDB
+Manual](https://docs.mongodb.com/manual/aggregation/) can be helpful here.
+
+A policy is basically two things:
+
+1. Description of what stages to add to pipeline in order to realize a `list`
+ query with a single aggregation pipeline
+2. A piece of logic that describes to the user why they are/are not allowed to
+ perform a given action.
+
+## Example
+
+```
+lang=typescript
+export default class MyPolicy extends Policy {
+ static type_name = "my-policy";
+ async _getRestrictingQuery(context: Context) {
+ if (context.user_id) {
+ return Query.fromSingleMatch({
+ "_metadata.created_context.user_id": { $eq: context.user_id },
+ });
+ }
+ return new DenyAll();
+ }
+ async checkerFunction(
+ context: Context,
+ item_getter: () => Promise<CollectionItem>
+ ) {
+ if ( some_other_condition ) {
+ return Policy.allow("you are who created this item");
+ } else {
+ return Policy.deny("you are not who created this item");
+ }
+ }
+ isItemSensitive = async () => true;
+}
+```
+
+## Methods
+
+### `_getRestrictingQuery`
+
+Returns a `Query` that selects //all the allowed items// from the given
+collection, considering the given context. `AllowAll` and `DenyAll` can be useful here.
+
+**Arguments**:
+
+- `context` - the context representing the user on whose behalf the action is performed.
+
+### `checkerFunction`
+
+Performs a check on a single item. This is run e.g. when viewing or editing a single item.
+
+Has to return `Policy.allow(reason)` or `Policy.deny(reason)`.
+
+**Arguments**:
+
+- `context` - the context representing the user on whose behalf the action is performed.
+- `item_getter` - a function that returns a promise with the item that this
+ action refers to. Only call it if you need the item data to make the decision.
diff --git a/src/main.ts b/src/main.ts
index 6fc818b1..95fe780e 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,33 +1,35 @@
export { default as Query } from "./datastore/query";
+export { default as DenyAll } from "./datastore/deny-all";
+export { AllowAll } from "./datastore/allow-all";
export { default as QueryTypes } from "./datastore/query-types";
export { default as SpecialFilter } from "./chip-types/special-filter";
export * as SpecialFilters from "./app/base-chips/special_filters/special-filters";
export { PolicyClass } from "./chip-types/policy";
export { default as Policy } from "./chip-types/policy";
export { default as Collection } from "./chip-types/collection";
export * as Policies from "./app/policy-types/policy-types";
export { default as Field } from "./chip-types/field";
export * from "./chip-types/field";
export * as FieldTypes from "./app/base-chips/field-types/field-types";
export { ActionName } from "./action";
export { default as App } from "./app/app";
export { default as Config } from "./app/config";
export { default as ConfigManager } from "./app/config-manager";
export { default as Logger } from "./app/logger";
export { default as Manifest } from "./app/manifest";
export { default as MetadataFactory } from "./app/metadata";
export { default as Context, SuperContext } from "./context";
export * as Queries from "./datastore/query";
export { default as HttpServer } from "./http/http";
export { default as i18nFactory } from "./i18n/i18n";
export { default as CalculatedField } from "./chip-types/calculated-field";
export * as EmailTemplates from "./email/templates/templates";
export * as Errors from "./response/errors";
export { default as File } from "./data-structures/file";
export { default as ItemList } from "./chip-types/item-list";
export { default as SMTPMailer } from "./email/smtp-mailer";
export { default as LoggerMailer } from "./email/logger-mailer";
export { default as Middlewares } from "./http/middlewares";
export { default as CollectionItem } from "./chip-types/collection-item";
export { EventDescription } from "./app/delegate-listener";
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 8, 08:37 (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1034367
Default Alt Text
(9 KB)
Attached To
Mode
rS Sealious
Attached
Detach File
Event Timeline
Log In to Comment