Page MenuHomeSealhub

No OneTemporary

diff --git a/README.md b/README.md
index 7e8e7fb4..0cc0700a 100644
--- a/README.md
+++ b/README.md
@@ -1,139 +1,143 @@
[![Sealious Logo](./src/assets/logo.png)](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

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)

Event Timeline