miolo 3.0.0-beta.152 → 3.0.0-beta.154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create/copy.mjs +2 -2
- package/bin/create/prepare-template.mjs +11 -0
- package/package.json +3 -2
- package/template/.agent/skills/miolo-app-arch/SKILL.md +251 -0
- package/template/.agent/skills/miolo-auth/SKILL.md +295 -0
- package/template/.agent/skills/miolo-cli-router/SKILL.md +394 -0
- package/template/.agent/skills/miolo-database/SKILL.md +358 -0
- package/template/.agent/skills/miolo-react-patterns/SKILL.md +426 -0
- package/template/.agent/skills/miolo-routing/SKILL.md +319 -0
- package/template/.agent/skills/miolo-schemas/SKILL.md +300 -0
- package/template/.agent/skills/miolo-session-context/SKILL.md +396 -0
- package/template/.agent/skills/miolo-ssr/SKILL.md +433 -0
- package/template/.env +6 -0
- package/template/.env.production +6 -0
- package/template/db/init.sh +15 -42
- package/template/package.json +6 -6
- package/template/src/server/miolo/db.mjs +9 -9
package/bin/create/copy.mjs
CHANGED
|
@@ -153,8 +153,8 @@ export function copyTemplate(sourcePath, destPath, appName, options = {}) {
|
|
|
153
153
|
fs.copyFileSync(srcPath, destItemPath)
|
|
154
154
|
}
|
|
155
155
|
} else if (stat.isDirectory()) {
|
|
156
|
-
//
|
|
157
|
-
if (item === 'src' || item === 'docker'|| item === 'db') {
|
|
156
|
+
// Copy src/, db/, docker/, and .agent/ directories
|
|
157
|
+
if (item === 'src' || item === 'docker' || item === 'db' || item === '.agent') {
|
|
158
158
|
copyDirectory(srcPath, destItemPath, appName, options)
|
|
159
159
|
}
|
|
160
160
|
}
|
|
@@ -74,6 +74,17 @@ for (const dir of dirsToCopy) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// Copy skills from workspace root
|
|
78
|
+
const skillsSrc = path.resolve(__dirname, '../../../../skills')
|
|
79
|
+
const skillsDest = path.join(templatePath, '.agent', 'skills')
|
|
80
|
+
if (fs.existsSync(skillsSrc)) {
|
|
81
|
+
fs.mkdirSync(path.join(templatePath, '.agent'), { recursive: true })
|
|
82
|
+
copyDirRecursive(skillsSrc, skillsDest)
|
|
83
|
+
console.log('[prepare-template] ✓ Copied skills/')
|
|
84
|
+
} else {
|
|
85
|
+
console.warn('[prepare-template] ⚠ Skills directory not found at', skillsSrc)
|
|
86
|
+
}
|
|
87
|
+
|
|
77
88
|
// Step 4: Update package.json versions
|
|
78
89
|
console.log('[prepare-template] Updating package.json versions...')
|
|
79
90
|
const templatePackageJsonPath = path.join(templatePath, 'package.json')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "miolo",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.154",
|
|
4
4
|
"description": "all-in-one koa-based server",
|
|
5
5
|
"author": "Donato Lorenzo <donato@afialapis.com>",
|
|
6
6
|
"contributors": [
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"template",
|
|
31
31
|
"template/.env",
|
|
32
32
|
"template/.env.production",
|
|
33
|
-
"template/.editorconfig"
|
|
33
|
+
"template/.editorconfig",
|
|
34
|
+
"template/.agent"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
|
36
37
|
"lint": "npx xeira lint ./src",
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: miolo-app-arch
|
|
3
|
+
description: Standard architecture patterns for miolo applications. Use when creating, modifying, or reviewing miolo applications to ensure proper project structure, directory organization, and file placement following miolo conventions established in miolo-sample.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Miolo Application Architecture
|
|
7
|
+
|
|
8
|
+
This skill defines the standard architecture for miolo applications based on miolo-sample, the reference implementation for miolo apps.
|
|
9
|
+
|
|
10
|
+
## Project Structure Overview
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
miolo-app/
|
|
14
|
+
├── package.json # npm scripts and dependencies
|
|
15
|
+
├── .env # Development environment variables
|
|
16
|
+
├── .env.production # Production environment variables
|
|
17
|
+
├── .editorconfig # Editor configuration
|
|
18
|
+
├── .gitignore # Git ignore rules
|
|
19
|
+
├── components.json # shadcn/ui configuration
|
|
20
|
+
├── jsconfig.json # JavaScript/Import path configuration
|
|
21
|
+
├── eslint.config.js # ESLint configuration
|
|
22
|
+
├── postcss.config.js # PostCSS/Tailwind configuration
|
|
23
|
+
├── docker/ # Docker deployment configuration
|
|
24
|
+
├── db/ # Database initialization (optional)
|
|
25
|
+
├── build/ # Production build output
|
|
26
|
+
├── src/ # Source code
|
|
27
|
+
│ ├── cli/ # Client-side React application
|
|
28
|
+
│ ├── ns/ # Shared code (namespace)
|
|
29
|
+
│ ├── server/ # Backend server code
|
|
30
|
+
│ └── static/ # Static assets
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Root Files
|
|
34
|
+
|
|
35
|
+
### package.json Scripts
|
|
36
|
+
|
|
37
|
+
Standard npm scripts for miolo development and deployment:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"scripts": {
|
|
42
|
+
"reset": "rm -fr node_modules package-lock.json && npm i",
|
|
43
|
+
"lint": "npx xeira lint src",
|
|
44
|
+
"dev": "npx miolo dev",
|
|
45
|
+
"deb": "npx miolo deb",
|
|
46
|
+
"create-bin": "npx miolo create-bin",
|
|
47
|
+
"build-client": "npx miolo build-client",
|
|
48
|
+
"build-server": "npx miolo build-server",
|
|
49
|
+
"build": "npm run build-client && npm run build-server && npm run create-bin",
|
|
50
|
+
"start": "node ./build/server/run.mjs start",
|
|
51
|
+
"stop": "node ./build/server/run.mjs stop",
|
|
52
|
+
"restart": "node ./build/server/run.mjs restart"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Key scripts:**
|
|
58
|
+
- `dev` - Development mode with hot reload
|
|
59
|
+
- `deb` - Development mode with hot reload and debugging
|
|
60
|
+
- `build` - Complete production build
|
|
61
|
+
- `start/stop/restart` - Production server management
|
|
62
|
+
|
|
63
|
+
### Import Aliases
|
|
64
|
+
|
|
65
|
+
Configure path aliases in `package.json`:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"imports": {
|
|
70
|
+
"#cli/*": "./src/cli/*",
|
|
71
|
+
"#ns/*": "./src/ns/*",
|
|
72
|
+
"#server/*": "./src/server/*",
|
|
73
|
+
"#static/*": "./src/static/*"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Use these aliases consistently in imports: `import Thing from '#server/routes/thing.mjs'`
|
|
79
|
+
|
|
80
|
+
### Environment Files
|
|
81
|
+
|
|
82
|
+
- `.env` - Development configuration (database, ports, session salt, etc.)
|
|
83
|
+
- `.env.production` - Production overrides
|
|
84
|
+
|
|
85
|
+
## Directory Structure
|
|
86
|
+
|
|
87
|
+
### docker/
|
|
88
|
+
|
|
89
|
+
Docker deployment configuration:
|
|
90
|
+
- `docker-compose.yaml` - Service orchestration
|
|
91
|
+
- `Dockerfile` - Container build instructions
|
|
92
|
+
|
|
93
|
+
### db/ (optional)
|
|
94
|
+
|
|
95
|
+
Database initialization scripts:
|
|
96
|
+
- `init.sh` - Database creation and setup script
|
|
97
|
+
- `sql/` - SQL migration files (executed in alphabetical order)
|
|
98
|
+
|
|
99
|
+
Not required for all apps, but recommended for PostgreSQL-based applications.
|
|
100
|
+
|
|
101
|
+
### build/
|
|
102
|
+
|
|
103
|
+
Production build output (generated, not versioned):
|
|
104
|
+
- `cli/` - Built client application
|
|
105
|
+
- `server/` - Built server application
|
|
106
|
+
|
|
107
|
+
Created by `npm run build`, used in production deployment.
|
|
108
|
+
|
|
109
|
+
### src/cli/
|
|
110
|
+
|
|
111
|
+
Client-side React application code. **Fixed subdirectory structure**:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
src/cli/
|
|
115
|
+
├── App.jsx # Root application component
|
|
116
|
+
├── entry-cli.jsx # Client entry point
|
|
117
|
+
├── index.html # HTML template
|
|
118
|
+
├── components/ # React components (shadcn/ui, custom)
|
|
119
|
+
│ ├── ui/ # shadcn/ui components
|
|
120
|
+
│ └── ... # Custom components
|
|
121
|
+
├── config/ # Client configuration
|
|
122
|
+
├── context/ # React contexts (data, session, theme, ui)
|
|
123
|
+
│ ├── data/
|
|
124
|
+
│ ├── session/
|
|
125
|
+
│ ├── theme/
|
|
126
|
+
│ └── ui/
|
|
127
|
+
├── hooks/ # Custom React hooks
|
|
128
|
+
├── layout/ # Layout components (sidebar, nav, etc.)
|
|
129
|
+
├── lib/ # Client utilities
|
|
130
|
+
└── pages/ # Page components
|
|
131
|
+
├── dash/ # Dashboard pages
|
|
132
|
+
├── offline/ # Unauthenticated pages (login, etc.)
|
|
133
|
+
└── ... # Feature-specific page directories
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Rules:**
|
|
137
|
+
- Respect the subdirectory structure
|
|
138
|
+
- Place new components in appropriate subdirectories
|
|
139
|
+
- Pages go in `pages/`, grouped by feature
|
|
140
|
+
- Reusable UI components in `components/`
|
|
141
|
+
- Contexts follow the established pattern (Context.jsx, Provider.jsx, useContext.mjs)
|
|
142
|
+
|
|
143
|
+
### src/ns/
|
|
144
|
+
|
|
145
|
+
Shared code accessible from both client and server:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
src/ns/
|
|
149
|
+
└── models/ # Data models
|
|
150
|
+
├── base/ # Base classes (BaseModel, BaseArray, etc.)
|
|
151
|
+
└── ... # Application-specific models
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Use for code that needs to run in both environments.
|
|
155
|
+
|
|
156
|
+
### src/server/
|
|
157
|
+
|
|
158
|
+
Backend server code. **Semi-fixed subdirectory structure**:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
src/server/
|
|
162
|
+
├── server.mjs # Server entry point
|
|
163
|
+
├── cache/ # Cache implementations
|
|
164
|
+
├── db/ # Database layer
|
|
165
|
+
│ ├── io/ # Database I/O operations (queries)
|
|
166
|
+
│ │ ├── users/ # User-related queries
|
|
167
|
+
│ │ ├── todos/ # Example: todo queries
|
|
168
|
+
│ │ └── ... # Feature-specific query directories
|
|
169
|
+
│ └── triggers/ # Database triggers
|
|
170
|
+
├── lib/ # Server libraries
|
|
171
|
+
├── miolo/ # Miolo server configuration
|
|
172
|
+
│ ├── auth/ # Authentication strategies
|
|
173
|
+
│ ├── cache.mjs # Cache configuration
|
|
174
|
+
│ ├── cron/ # Cron jobs
|
|
175
|
+
│ ├── db.mjs # Database configuration
|
|
176
|
+
│ ├── http.mjs # HTTP configuration
|
|
177
|
+
│ ├── index.mjs # Main miolo config
|
|
178
|
+
│ ├── routes/ # Base route configuration
|
|
179
|
+
│ └── ssr/ # Server-side rendering
|
|
180
|
+
├── routes/ # API endpoints (developers add here)
|
|
181
|
+
│ ├── index.mjs # Route registration
|
|
182
|
+
│ ├── users/ # User endpoints
|
|
183
|
+
│ └── ... # Feature-specific route directories
|
|
184
|
+
└── utils/ # Server utilities
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Rules:**
|
|
188
|
+
- `cache/`, `lib/`, `utils/`, `miolo/` are relatively fixed
|
|
189
|
+
- **Developers add new API endpoints in `routes/`**
|
|
190
|
+
- **Database queries go in `db/io/`**, organized by domain
|
|
191
|
+
- Each route directory contains endpoint handlers
|
|
192
|
+
- Register new routes in `routes/index.mjs`
|
|
193
|
+
|
|
194
|
+
**Key pattern:** When adding a new feature:
|
|
195
|
+
1. Create queries in `db/io/feature-name/`
|
|
196
|
+
2. Create route handlers in `routes/feature-name/`
|
|
197
|
+
3. Register routes in `routes/index.mjs`
|
|
198
|
+
|
|
199
|
+
### src/static/
|
|
200
|
+
|
|
201
|
+
Static assets:
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
src/static/
|
|
205
|
+
├── fonts/ # Custom fonts
|
|
206
|
+
├── img/ # Images
|
|
207
|
+
│ ├── default/ # Default images (avatars, etc.)
|
|
208
|
+
│ └── ...
|
|
209
|
+
├── public/ # Public root files (favicon, robots.txt)
|
|
210
|
+
└── style/ # Global CSS
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Development Workflow
|
|
214
|
+
|
|
215
|
+
1. **Create app**: `npx miolo create <app-name>`
|
|
216
|
+
2. **Develop**: `npm run dev` (hot reload on port from .env)
|
|
217
|
+
3. **Build**: `npm run build` (creates production build/)
|
|
218
|
+
4. **Deploy**: Use docker/ configuration or build/server/run.mjs
|
|
219
|
+
|
|
220
|
+
## Best Practices
|
|
221
|
+
|
|
222
|
+
1. **Respect directory structure** - Don't create new top-level directories without reason
|
|
223
|
+
2. **Use import aliases** - Always use `#cli/`, `#server/`, etc. for imports
|
|
224
|
+
3. **Follow naming conventions** - Use lowercase with hyphens for directories, camelCase for files
|
|
225
|
+
4. **Organize by feature** - In routes/ and db/io/, group by domain/feature
|
|
226
|
+
5. **Keep server config in miolo/** - Don't modify unless necessary
|
|
227
|
+
6. **Use contexts for state** - Follow established context patterns in cli/context/
|
|
228
|
+
|
|
229
|
+
## Common Patterns
|
|
230
|
+
|
|
231
|
+
**Adding a new feature:**
|
|
232
|
+
```
|
|
233
|
+
1. Add database queries: src/server/db/io/myfeature/*.mjs
|
|
234
|
+
2. Add API routes: src/server/routes/myfeature/*.mjs
|
|
235
|
+
3. Register routes: src/server/routes/index.mjs
|
|
236
|
+
4. Add client pages: src/cli/pages/myfeature/*.jsx
|
|
237
|
+
5. Add components if needed: src/cli/components/myfeature/*.jsx
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Adding authentication:**
|
|
241
|
+
- Strategies in `src/server/miolo/auth/`
|
|
242
|
+
- Configure in `src/server/miolo/index.mjs`
|
|
243
|
+
|
|
244
|
+
**Adding static assets:**
|
|
245
|
+
- Images: `src/static/img/`
|
|
246
|
+
- Fonts: `src/static/fonts/`
|
|
247
|
+
- Root files: `src/static/public/`
|
|
248
|
+
|
|
249
|
+
## Architecture created by `npx miolo create`
|
|
250
|
+
|
|
251
|
+
The `miolo create` command generates this structure automatically. Maintain it throughout development to ensure consistency with miolo conventions and compatibility with miolo tooling.
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: miolo-auth
|
|
3
|
+
description: Authentication configuration and strategies for miolo applications. Use when implementing, modifying, or troubleshooting authentication, configuring auth strategies (credentials, basic, guest), or setting up user sessions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Miolo Authentication
|
|
7
|
+
|
|
8
|
+
Authentication configuration and strategies for miolo applications.
|
|
9
|
+
|
|
10
|
+
## Authentication Strategies
|
|
11
|
+
|
|
12
|
+
Miolo supports multiple built-in authentication strategies in `src/server/miolo/auth/`:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
src/server/miolo/auth/
|
|
16
|
+
├── credentials.mjs # Username/password authentication
|
|
17
|
+
├── basic.mjs # HTTP Basic authentication
|
|
18
|
+
├── guest.mjs # Guest/anonymous access
|
|
19
|
+
└── custom.mjs # Custom authentication logic
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configuring Authentication
|
|
23
|
+
|
|
24
|
+
Authentication is configured in `src/server/miolo/index.mjs`:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import auth from './auth/credentials.mjs'
|
|
28
|
+
// or: import auth from './auth/basic.mjs'
|
|
29
|
+
// or: import auth from './auth/guest.mjs'
|
|
30
|
+
|
|
31
|
+
export default {
|
|
32
|
+
auth,
|
|
33
|
+
// ... other config
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Credentials Strategy (Default)
|
|
38
|
+
|
|
39
|
+
Username/password authentication with database-backed users.
|
|
40
|
+
|
|
41
|
+
**File:** `src/server/miolo/auth/credentials.mjs`
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
import { db_user_auth } from '#server/db/io/users/auth.mjs'
|
|
45
|
+
|
|
46
|
+
export default {
|
|
47
|
+
urls: {
|
|
48
|
+
login: '/page/login',
|
|
49
|
+
logout: '/page/logout',
|
|
50
|
+
home: '/'
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async login(ctx, credentials) {
|
|
54
|
+
const { username, password } = credentials
|
|
55
|
+
|
|
56
|
+
const user = await db_user_auth(ctx, { username, password })
|
|
57
|
+
|
|
58
|
+
if (!user) {
|
|
59
|
+
return { ok: false, error: 'Invalid credentials' }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { ok: true, user }
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
async logout(ctx) {
|
|
66
|
+
// Cleanup if needed
|
|
67
|
+
return { ok: true }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Key elements:**
|
|
73
|
+
- `urls` - Define login, logout, home page paths
|
|
74
|
+
- `login(ctx, credentials)` - Validates credentials, returns user or error
|
|
75
|
+
- `logout(ctx)` - Optional cleanup on logout
|
|
76
|
+
- Uses database function from `db/io/users/auth.mjs`
|
|
77
|
+
|
|
78
|
+
## Basic Authentication
|
|
79
|
+
|
|
80
|
+
HTTP Basic Auth for simple API access.
|
|
81
|
+
|
|
82
|
+
**File:** `src/server/miolo/auth/basic.mjs`
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
export default {
|
|
86
|
+
async login(ctx, credentials) {
|
|
87
|
+
const { username, password } = credentials
|
|
88
|
+
|
|
89
|
+
// Validate against environment or database
|
|
90
|
+
if (username === process.env.API_USER &&
|
|
91
|
+
password === process.env.API_PASSWORD) {
|
|
92
|
+
return {
|
|
93
|
+
ok: true,
|
|
94
|
+
user: { id: 1, username, role: 'api' }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { ok: false, error: 'Invalid credentials' }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Guest/Anonymous Access
|
|
104
|
+
|
|
105
|
+
Allow unauthenticated access (useful for read-only apps).
|
|
106
|
+
|
|
107
|
+
**File:** `src/server/miolo/auth/guest.mjs`
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
export default {
|
|
111
|
+
async login(ctx) {
|
|
112
|
+
return {
|
|
113
|
+
ok: true,
|
|
114
|
+
user: { id: 0, username: 'guest', role: 'guest' }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## User Session
|
|
121
|
+
|
|
122
|
+
After successful login, user is available in routes:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
export async function r_protected_route(ctx, params) {
|
|
126
|
+
const user = ctx.state.user
|
|
127
|
+
|
|
128
|
+
// User object contains:
|
|
129
|
+
// - id
|
|
130
|
+
// - username
|
|
131
|
+
// - email
|
|
132
|
+
// - any other fields returned from login
|
|
133
|
+
|
|
134
|
+
ctx.miolo.logger.info(`User ${user.id} accessing route`)
|
|
135
|
+
|
|
136
|
+
return { ok: true, data: { userId: user.id } }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Protecting Routes
|
|
141
|
+
|
|
142
|
+
Routes are protected by adding `auth` configuration:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
const auth = {
|
|
146
|
+
require: true,
|
|
147
|
+
action: 'redirect',
|
|
148
|
+
redirect_url: '/page/login'
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export default [{
|
|
152
|
+
prefix: 'api',
|
|
153
|
+
routes: [
|
|
154
|
+
// Public route
|
|
155
|
+
{ method: 'GET', url: '/public/data', callback: r_public },
|
|
156
|
+
|
|
157
|
+
// Protected route
|
|
158
|
+
{ method: 'GET', url: '/user/profile', auth, callback: r_profile },
|
|
159
|
+
]
|
|
160
|
+
}]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Database User Authentication
|
|
164
|
+
|
|
165
|
+
User authentication typically queries the database:
|
|
166
|
+
|
|
167
|
+
**File:** `src/server/db/io/users/auth.mjs`
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
import { sha512 } from '#server/utils/crypt.mjs'
|
|
171
|
+
|
|
172
|
+
export async function db_user_auth(ctx, params) {
|
|
173
|
+
const { username, password } = params
|
|
174
|
+
|
|
175
|
+
const salt = process.env.MIOLO_SESSION_SALT
|
|
176
|
+
const hashedPassword = sha512(password, salt)
|
|
177
|
+
|
|
178
|
+
const user = await ctx.miolo.db.query(`
|
|
179
|
+
SELECT id, username, email, name
|
|
180
|
+
FROM u_user
|
|
181
|
+
WHERE username = $1 AND password = $2
|
|
182
|
+
`, [username, hashedPassword])
|
|
183
|
+
|
|
184
|
+
return user.rows[0] || null
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Key elements:**
|
|
189
|
+
- Hash password with session salt
|
|
190
|
+
- Query user by username and hashed password
|
|
191
|
+
- Return user object or null
|
|
192
|
+
- Don't return password in user object
|
|
193
|
+
|
|
194
|
+
## Password Hashing
|
|
195
|
+
|
|
196
|
+
Use the provided crypto utilities:
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
import { sha512 } from '#server/utils/crypt.mjs'
|
|
200
|
+
|
|
201
|
+
const salt = process.env.MIOLO_SESSION_SALT
|
|
202
|
+
const hashedPassword = sha512(plainPassword, salt)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Session salt** is configured in `.env`:
|
|
206
|
+
```
|
|
207
|
+
MIOLO_SESSION_SALT=your-random-salt-here
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Changing Password
|
|
211
|
+
|
|
212
|
+
Pattern for password change:
|
|
213
|
+
|
|
214
|
+
**File:** `src/server/db/io/users/pwd.mjs`
|
|
215
|
+
|
|
216
|
+
```javascript
|
|
217
|
+
import { sha512 } from '#server/utils/crypt.mjs'
|
|
218
|
+
|
|
219
|
+
export async function db_user_change_password(ctx, params) {
|
|
220
|
+
const { user_id, old_password, new_password } = params
|
|
221
|
+
|
|
222
|
+
const salt = process.env.MIOLO_SESSION_SALT
|
|
223
|
+
|
|
224
|
+
// Verify old password
|
|
225
|
+
const user = await ctx.miolo.db.query(`
|
|
226
|
+
SELECT id FROM u_user
|
|
227
|
+
WHERE id = $1 AND password = $2
|
|
228
|
+
`, [user_id, sha512(old_password, salt)])
|
|
229
|
+
|
|
230
|
+
if (user.rows.length === 0) {
|
|
231
|
+
throw new Error('Invalid current password')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Update to new password
|
|
235
|
+
const newHash = sha512(new_password, salt)
|
|
236
|
+
await ctx.miolo.db.query(`
|
|
237
|
+
UPDATE u_user
|
|
238
|
+
SET password = $1
|
|
239
|
+
WHERE id = $2
|
|
240
|
+
`, [newHash, user_id])
|
|
241
|
+
|
|
242
|
+
return { changed: true }
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Custom Authentication
|
|
247
|
+
|
|
248
|
+
For complex scenarios, implement custom logic:
|
|
249
|
+
|
|
250
|
+
**File:** `src/server/miolo/auth/custom.mjs`
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
export default {
|
|
254
|
+
urls: {
|
|
255
|
+
login: '/auth/custom',
|
|
256
|
+
logout: '/auth/logout',
|
|
257
|
+
home: '/'
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
async login(ctx, credentials) {
|
|
261
|
+
// Custom logic: OAuth, LDAP, etc.
|
|
262
|
+
const user = await yourCustomAuthLogic(credentials)
|
|
263
|
+
|
|
264
|
+
if (!user) {
|
|
265
|
+
return { ok: false, error: 'Authentication failed' }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return { ok: true, user }
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
async logout(ctx) {
|
|
272
|
+
// Custom cleanup
|
|
273
|
+
await yourCustomLogoutLogic(ctx)
|
|
274
|
+
return { ok: true }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Best Practices
|
|
280
|
+
|
|
281
|
+
1. **Never store plain passwords** - Always hash with salt
|
|
282
|
+
2. **Use strong session salt** - Set `MIOLO_SESSION_SALT` in `.env`
|
|
283
|
+
3. **Don't return passwords** - User object should never include password
|
|
284
|
+
4. **Validate on server** - Never trust client-side authentication
|
|
285
|
+
5. **Use HTTPS in production** - Protect credentials in transit
|
|
286
|
+
6. **Rate limit login attempts** - Prevent brute force attacks
|
|
287
|
+
7. **Log authentication events** - Track successful and failed logins
|
|
288
|
+
|
|
289
|
+
## Examples from miolo-sample
|
|
290
|
+
|
|
291
|
+
See actual implementations:
|
|
292
|
+
- `src/server/miolo/auth/credentials.mjs` - Default auth strategy
|
|
293
|
+
- `src/server/db/io/users/auth.mjs` - User authentication query
|
|
294
|
+
- `src/server/db/io/users/pwd.mjs` - Password change logic
|
|
295
|
+
- `src/server/utils/crypt.mjs` - Hashing utilities
|