Package Exports
- stackpress
- stackpress/Attribute
- stackpress/Column
- stackpress/Exception
- stackpress/Fieldset
- stackpress/Language
- stackpress/Model
- stackpress/Registry
- stackpress/Revisions
- stackpress/Session
- stackpress/Terminal
- stackpress/admin
- stackpress/admin/pages/create
- stackpress/admin/pages/detail
- stackpress/admin/pages/detail/create
- stackpress/admin/pages/detail/search
- stackpress/admin/pages/export
- stackpress/admin/pages/import
- stackpress/admin/pages/remove
- stackpress/admin/pages/restore
- stackpress/admin/pages/search
- stackpress/admin/pages/update
- stackpress/admin/plugin
- stackpress/admin/transform
- stackpress/admin/types
- stackpress/api
- stackpress/api/plugin
- stackpress/api/types
- stackpress/api/views/oauth
- stackpress/assert
- stackpress/assets.d.ts
- stackpress/cjs/api/views/oauth
- stackpress/cjs/session/views/signin
- stackpress/cjs/session/views/signup
- stackpress/client
- stackpress/client/plugin
- stackpress/client/transform
- stackpress/client/types
- stackpress/email
- stackpress/email/plugin
- stackpress/email/types
- stackpress/esm/api/views/oauth
- stackpress/esm/session/views/signin
- stackpress/esm/session/views/signup
- stackpress/http
- stackpress/language
- stackpress/language/plugin
- stackpress/language/types
- stackpress/lib
- stackpress/mysql
- stackpress/pglite
- stackpress/pgsql
- stackpress/plugin
- stackpress/schema
- stackpress/schema/types
- stackpress/scripts
- stackpress/scripts/build
- stackpress/scripts/drop
- stackpress/scripts/emit
- stackpress/scripts/generate
- stackpress/scripts/index
- stackpress/scripts/install
- stackpress/scripts/migrate
- stackpress/scripts/purge
- stackpress/scripts/push
- stackpress/scripts/serve
- stackpress/server
- stackpress/server/types
- stackpress/session
- stackpress/session/plugin
- stackpress/session/types
- stackpress/session/views/signin
- stackpress/session/views/signup
- stackpress/sql
- stackpress/sql/actions
- stackpress/sql/actions/batch
- stackpress/sql/actions/create
- stackpress/sql/actions/detail
- stackpress/sql/actions/get
- stackpress/sql/actions/index
- stackpress/sql/actions/remove
- stackpress/sql/actions/restore
- stackpress/sql/actions/search
- stackpress/sql/actions/update
- stackpress/sql/actions/upsert
- stackpress/sql/events
- stackpress/sql/events/batch
- stackpress/sql/events/create
- stackpress/sql/events/detail
- stackpress/sql/events/get
- stackpress/sql/events/index
- stackpress/sql/events/purge
- stackpress/sql/events/remove
- stackpress/sql/events/restore
- stackpress/sql/events/search
- stackpress/sql/events/update
- stackpress/sql/events/upsert
- stackpress/sql/plugin
- stackpress/sql/transform
- stackpress/sql/types
- stackpress/sqlite
- stackpress/stackpress.css
- stackpress/terminal
- stackpress/terminal/events
- stackpress/terminal/events/build
- stackpress/terminal/events/drop
- stackpress/terminal/events/emit
- stackpress/terminal/events/generate
- stackpress/terminal/events/index
- stackpress/terminal/events/install
- stackpress/terminal/events/migrate
- stackpress/terminal/events/purge
- stackpress/terminal/events/push
- stackpress/terminal/events/query
- stackpress/terminal/events/serve
- stackpress/terminal/plugin
- stackpress/terminal/types
- stackpress/tsconfig/cjs
- stackpress/tsconfig/esm
- stackpress/types
- stackpress/types/plugin
- stackpress/types/transform
- stackpress/unocss
- stackpress/view
- stackpress/view/client
- stackpress/view/import
- stackpress/view/plugin
- stackpress/view/transform
- stackpress/view/types
- stackpress/whatwg
Readme
Stackpress
Stackpress is a content management framework. It combines several open source projects maintained by the Stackpress Team.
Features
The goal of Stackpress is to build robust apps in days, not months.
- Admin Generator
- React Template Engine
- Built-in SQL tools
- Built-in User, Auth, Session, API, and Email plugins
Nuances
Unlike other frameworks, we have chosen the following philosophies.
- Server Side First
- TS/ESM/CJS
- Server/less framework
- Unopinionated
- Event Driven
- Plugggable Design
- Client Generator
Usage
import { server } from 'stackpress/http'
const app = server()
app.get('/', (req, res) => {
const name = req.data.path('name', 'guest')
res.setBody('text/plain', `Hello ${name}`)
})
app.create().listen(3000, () => {
console.log('Server is running on port 3000')
})
See Example for use case.
Routing
Stackpress uses several routers for callbacks, lazy imports and React templates. You can access each router like the following.
//action routing
app.action.get('/home', (req, res, ctx) => {});
//import routing
app.import.get('/home', () => import('./pages/home'));
//view routing (react)
app.view.get('/home', '@/views/home');
Like other server frameworks, the following route methods are available.
//common methods
app.connect('/home', action)
app.delete('/home', action)
app.get('/home', action)
app.head('/home', action)
app.options('/home', action)
app.patch('/home', action)
app.post('/home', action)
app.put('/home', action)
app.trace('/home', action)
//any method
app.all('/home', action)
//custom method
app.route('get', '/home')
You can let Stackpress infer your action implementation.
//action callback
app.get('/home', (req, res, ctx) => {});
//lazy import action
app.get('/home', () => import('./pages/home'));
//react template
app.get('/home', '@/views/home');
You can prioritize routing in the following way where the highest number calls first.
//first
app.get('/home', action, 100);
//second
app.get('/home', action);
//third
app.get('/home', action);
//fourth
app.get('/home', action, -100);
Events
Routing is built on top of events, a necessity to event driven design.
app.on('email-send', logInDatabase)
app.on('email-send', smtpQueue)
app.on('email-send', updateDatabase)
Like routing, you can use action, imports and views.
//action callback
app.action.on('email-send', logInDatabase)
//use a separate file
app.import.on('email-send', () => import('/event/smtp-queue'))
//set the response view
app.view.on('email-send', '@/views/email/success')
Database
A minimal database setup would look like the following.
import path from 'node:path'
import { PGlite } from '@electric-sql/pglite'
import { connect } from 'stackpress/pglite'
const db = await connect(async () => {
return new PGlite(path.resolve(cwd, 'database'))
})
app.register('database', db);
Stackpress has support for the following SQL packages.
mysql2
pg
pglite
better-sqlite3
No matter the SQL package, the query builder is exactly the same. See the following example.
const products = await db.select('*').from('products').where('price > %s', 100)
You can do a raw query like this as well.
const users = await db.query('SELECT * from users')
And you can do transactions like this.
await db.transaction(async () => {
await db.update('users').set({ age: 100 }).where('age > %s', 100)
})
Plugin
The following is an example of a basic plugin you can create in your
project or use from a Stackpress project in node_modules
.
//plugin.ts
import type { Server } from 'stackpress/server'
export default function MyPlugin(app: Server) {
//start plugging...
app.on('email-send', logInDatabase)
app.get('/signin', () => import('/page/signin'))
}
While developing a plugin, you can get the project configuration like this.
export default function MyPlugin(app: Server) {
//get all the config
const config = app.config()
//traverse the config
const cwd = app.config<string>('server', 'cwd')
//use dot pathing (default production)
const mode = app.config.path<string>('server.mode', 'production')
}
You can register your plugin so other plugins can access like this.
export default function MyPlugin(app: Server) {
app.register('my-plugin', { foo: 'bar' })
}
You can add plugins by manually importing them xor using package.json
.
{
"name": "my-project",
"plugins": [ "./plugin", "stackpress" ],
"dependencies": {...}
}
To load (initialize) plugins you just need to run this code.
await app.bootstrap();
Then you can access plugins in your project like this.
const myPlugin = app.plugin('my-plugin')
//or
const myPlugin2 = app.plugin<{ foo: string }>('my-plugin')
Admin
To generate an admin you need to first create a schema.idea
file in
the root of your project.
model User @label("User" "Users") @template("{{name}}") @icon("user") {
id String @label("ID")
@id @default("cuid()")
@list.overflow({ length 10 hellip true })
name String @label("Name")
@searchable
@field.text
@is.required
@list.text @view.text
}
Next emit emit following events.
await app.emit('config')
await app.emit('listen')
await app.emit('route')
Next export a const config
in your project root with the following configuration.
export const config = {
client: {
//used by `stackpress/client` to `import()`
//the generated client code to memory
module: '.client',
//where to store the generated client code
build: path.join(cwd, 'node_modules', '.client'),
//what tsconfig file to base the typescript compiler on
tsconfig: path.join(cwd, 'tsconfig.json')
}
}
Then in terminal you can run the following.
$ npx stackpress index generate
This will generate a .client
folder in node_modules
. You can check
the admin by visiting http://localhost:3000/admin/user/search
. You can
also access the ORM like the following.
import type { ClientPlugin } from 'stackpress'
const client = app.plugin<ClientPlugin>('client')
const user = await client.model.user.create({ name: 'John Doe' })
New events will be available as well.
import type { User } from '.client'
await app.resolve<User>('user-create', { name: 'John Doe' })
Authentication
To use the default authentication, you need to add an auth
configuration.
app.config.set('auth', {
//base route for signin, signout, signup pages
base: '/auth',
//default roles for new users
roles: [ 'USER' ],
//allow signin with username
username: true,
//allow signin with email address
email: true,
//allow signin with phone number
phone: true
})
Next you need to import the stackpress.idea
file to your main
schema.idea
.
use "stackpress/stackpress.idea"
//your models here...
Lastly run the following in terminal.
$ npx stackpress transform
$ npx stackpress push
Permissions
When you add a session
configuration, your project will deny access to
all pages (blacklist by default). You can open/configure route access
by roles using just the configuration.
app.config.set('session', {
//name of the session cookie
key: 'session',
//used to generate the session id
//also used to encrypt/decrypt data
//in the database
seed: 'ABC123',
access: {
//role: permissions
GUEST: [
//dev routes
{ method: 'ALL', route: '/@vite/client' },
{ method: 'ALL', route: '/@react-refresh' },
{ method: 'ALL', route: '/@fs/**' },
{ method: 'ALL', route: '/node_modules/**' },
{ method: 'ALL', route: '/__uno.css' },
{ method: 'ALL', route: '/plugins/**' },
{ method: 'ALL', route: '/react.svg' },
//public routes
{ method: 'GET', route: '/assets/**' },
{ method: 'GET', route: '/client/**' },
{ method: 'GET', route: '/images/**' },
{ method: 'GET', route: '/styles/**' },
{ method: 'GET', route: '/favicon.ico' },
{ method: 'GET', route: '/favicon.png' },
//page routes
{ method: 'GET', route: '/' },
{ method: 'ALL', route: '/auth/**' },
{ method: 'ALL', route: '/admin/**' },
{ method: 'ALL', route: '/api/**' }
]
}
})
By default everyone is a GUEST
and other role names are arbitrary.