Skip to content

Commit 34fdf3a

Browse files
committed
doc: improve docs organization
- Move extension docs back into this repo - Add index README.md under `doc/` - Improve documentation of project structure
1 parent 8ed1f80 commit 34fdf3a

File tree

11 files changed

+357
-28
lines changed

11 files changed

+357
-28
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ npm start
5050

5151
This will launch Puter at http://puter.localhost:4100 (or the next available port).
5252

53-
If this does not work, see [First Run Issues](./doc/first-run-issues.md) for
53+
If this does not work, see [First Run Issues](./doc/self-hosters/first-run-issues.md) for
5454
troubleshooting steps.
5555

5656
<br/>

doc/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## User Documentation
2+
3+
- [Hosting Instructions](./self-hosters/instructions.md)
4+
- [Domain Setup](./self-hosters/domains.md)
5+
- [Support Levels](./self-hosters/support.md)
6+
7+
## Contributor Documentation
8+
9+
### Where to Start
10+
11+
Start with [Repo Structure and Tooling](./contributors/structure.md).
12+
13+
### Index
14+
15+
- **Conventions**
16+
- [Repo Structure and Tooling](./contributors/structure.md)
17+
- How directories and files are organized in our GitHub repo
18+
- What tools are used to build parts of Puter
19+
- [Comment Prefixes](./contributors/comment_prefixes.md)
20+
- A convention we use for line comments in code
21+
22+
- [Frontend Documentation](/src/gui/doc)
23+
- [Backend Documentation](/src/backend/doc)
24+
- [Extensions](./contributors/extensions/)

doc/contributors/extensions.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1-
## Puter Extensions
1+
# Puter Extensions
22

3-
See the [Wiki Page](https://github.com/HeyPuter/puter/wiki/ex_extensions)
3+
## Quickstart
4+
5+
Create and edit this file: `mods/mods_enabled/hello-puter.js`
6+
7+
```javascript
8+
const { UserActorType, AppUnderUserActorType } = use.core;
9+
10+
extension.get('/hello-puter', (req, res) => {
11+
const actor = req.actor;
12+
let who = 'unknown';
13+
if ( actor.type instanceof UserActorType ) {
14+
who = actor.type.user.username;
15+
}
16+
if ( actor.type instanceof AppUnderUserActorType ) {
17+
who = actor.type.app.name + ' on behalf of ' + actor.type.user.username;
18+
}
19+
res.send(`Hello, ${who}!`);
20+
});
21+
```
22+
23+
## Events
24+
25+
//
26+
27+
This is subject to change as we make efforts to simplify the process.
28+
29+
### Step 1: Configure a Mod Directory
30+
31+
Add this to your config:
32+
```json
33+
"mod_directories": [
34+
"{source}/../mods/mods_available"
35+
]
36+
```
37+
38+
This adds the `mods/mods_available` directory to this
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Puter Extensions
2+
3+
## Quickstart
4+
5+
Create and edit this file: `mods/mods_enabled/hello-puter.js`
6+
7+
```javascript
8+
// You can get definitions exposed by Puter via `use`
9+
const { UserActorType, AppUnderUserActorType } = use.core;
10+
11+
// Endpoints can be registered directly on an extension
12+
extension.get('/hello-puter', (req, res) => {
13+
const actor = req.actor;
14+
15+
16+
// Make a string "who" which says:
17+
// "<username>", or:
18+
// "<app> acting on behalf of <username>"
19+
let who = 'unknown';
20+
if ( actor.type instanceof UserActorType ) {
21+
who = actor.type.user.username;
22+
}
23+
if ( actor.type instanceof AppUnderUserActorType ) {
24+
who = actor.type.app.name
25+
+ ' on behalf of '
26+
+ actor.type.user.username;
27+
}
28+
29+
res.send(`Hello, ${who}!`);
30+
});
31+
32+
// Extensions can listen to events and manipulate Puter's behavior
33+
extension.on('core.email.validate', event => {
34+
if ( event.email.includes('evil') ) {
35+
event.allow = false;
36+
}
37+
});
38+
```
39+
40+
### Scope of `extension` and `use`
41+
42+
It is important to know that the `extension` global is temporary and does not
43+
exist after your extension is loaded. If you wish to access the extension
44+
object within a callback you will need to first bind it to a variable in
45+
your extension's scope.
46+
47+
```javascript
48+
const ext = extension;
49+
extension.on('some-event', () => {
50+
// This would throw an error
51+
// extension.something();
52+
53+
// This works
54+
ext.example();
55+
})
56+
```
57+
58+
The same is true for `use`. Calls to `use` should happen at the top of
59+
the file, just like imports in ES6.
60+
61+
## Database Access
62+
63+
A database access object is provided to the extension via `extension.db`.
64+
You **must** scope `extension` to another variable (`ext` in this example)
65+
in order to access `db` from callbacks.
66+
67+
```javascript
68+
const ext = extension;
69+
70+
extension.get('/user-count', { noauth: true }, (req, res) => {
71+
const [count] = await ext.db.read(
72+
'SELECT COUNT(*) as c FROM `user`'
73+
);
74+
});
75+
```
76+
77+
The database access object has the following methods:
78+
- `read(query, params)` - read from the database using a prepared statement. If read-replicas are enabled, this will use a replica.
79+
- `write(query, params)` - write to the database using a prepared statement. If read-replicas are enabled, this will write to the primary.
80+
- `pread(query, params)` - read from the database using a prepared statement. If read-replicas are enabled, this will read from the primary.
81+
- `requireRead(query, params)` - read from the database using a prepared statement. If read-replicas are enabled, this will try reading from the replica first. If there are no results, a second attempt will be made on the primary.
82+
83+
## Events
84+
85+
See [events.md](./events.md)
86+
87+
## Definitions
88+
89+
See [definitions.md](./definitions.md)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
## Definitions
2+
3+
### `core.config` - Configuration
4+
5+
Puter's configuration object. This includes values from `config.json` or their
6+
defaults, and computed values like `origin` and `api_origin`.
7+
8+
```javascript
9+
const config = use('core.config');
10+
11+
extension.get('/get-origin', { noauth: true }, (req, res) => {
12+
res.send(config.origin);
13+
})
14+
```
15+
16+
### `core.util.*` - Utility Functions
17+
18+
These utilities come from `src/backend/src/util` in Puter's repo.
19+
Each file in this directory has its exports auto-loaded into this
20+
namespace. For example, `src/backend/src/util/langutil.js` is available
21+
via `use('core.util.langutil')` or `use.core.util.langutil`.
22+
23+
#### `core.util.helpers` - Helper Functions
24+
25+
Common utility functions used throughout Puter's backend. Use with caution as
26+
some of these functions may be deprecated.
27+
28+
> **note:** the following documentation is incomplete
29+
30+
#### `core.util.langutil` - Language Helpers
31+
32+
##### `whatis(thing :any)`
33+
34+
- Returns `"array"` if `thing` is an array.
35+
- Returns `"null"` if `thing` is `null`.
36+
- Returns `typeof thing` for any other case.
37+
38+
##### `nou(value :any)`
39+
40+
Simply a "null or undefined" check.
41+
42+
##### `can(value :any, capabilities :Array<string>)`
43+
44+
Checks if something has the specified capabilities. At the time of
45+
writing the only one supported is `iterate`, which will check if
46+
`value[Symbol.iterator]` is truthy
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module.exports = [
2+
{
3+
id: 'core.email.validate',
4+
description: `
5+
This event is emitted when an email is being validated.
6+
The event can be used to block certain emails from being validated.
7+
`,
8+
properties: {
9+
email: {
10+
type: 'string',
11+
mutability: 'no-effect',
12+
summary: 'the email being validated',
13+
notes: [
14+
'The email may have already been cleaned.',
15+
]
16+
},
17+
allow: {
18+
type: 'boolean',
19+
mutability: 'mutable',
20+
summary: 'whether the email is allowed',
21+
notes: [
22+
'If set to false, the email will be considered invalid.',
23+
]
24+
},
25+
},
26+
}
27+
];
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
### `core.email.validate`
2+
3+
This event is emitted when an email is being validated.
4+
The event can be used to block certain emails from being validated.
5+
6+
#### Property `email`
7+
8+
the email being validated
9+
- **Type**: string
10+
- **Mutability**: no-effect
11+
- **Notes**: undefined
12+
- The email may have already been cleaned.
13+
#### Property `allow`
14+
15+
whether the email is allowed
16+
- **Type**: boolean
17+
- **Mutability**: mutable
18+
- **Notes**: undefined
19+
- If set to false, the email will be considered invalid.
20+
21+
### `core.request.measured`
22+
23+
This event is emitted when a requests incoming and outgoing bytes
24+
have been measured.
25+
26+
```javascript
27+
extension.on('core.request.measured', data => {
28+
const measurements = data.measurements;
29+
// measurements = { sz_incoming: integer, sz_outgoing: integer }
30+
31+
const actor = data.actor; // instance of Actor
32+
33+
console.log('\x1B[36;1m === MEASUREMENT ===\x1B[0m\n', {
34+
actor: data.actor.uid,
35+
measurements: data.measurements
36+
});
37+
});
38+
```

doc/contributors/extensions/gen.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const dedent = require('dedent');
2+
const events = require('./events.json.js');
3+
4+
const mdlib = {};
5+
mdlib.h = (out, n, str) => {
6+
out(`${'#'.repeat(n)} ${str}\n\n`);
7+
}
8+
9+
const N_START = 3;
10+
11+
const out = str => process.stdout.write(str);
12+
for ( const event of events ) {
13+
mdlib.h(out, N_START, `\`${event.id}\``);
14+
out(dedent(event.description) + '\n\n');
15+
16+
for ( const k in event.properties ) {
17+
const prop = event.properties[k];
18+
mdlib.h(out, N_START + 1, `Property \`${k}\``);
19+
out(prop.summary + '\n');
20+
out(`- **Type**: ${prop.type}\n`);
21+
out(`- **Mutability**: ${prop.mutability}\n`);
22+
if ( prop.notes ) {
23+
out(`- **Notes**: ${prop.n}\n`);
24+
for ( const note of prop.notes ) {
25+
out(` - ${note}\n`);
26+
}
27+
}
28+
}
29+
30+
}

doc/contributors/index.md

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)