tosijs-platform 1.0.0
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/LICENSE +21 -0
- package/README.md +552 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Tonio Loewald
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
# tosijs-platform
|
|
2
|
+
|
|
3
|
+
## The Problem
|
|
4
|
+
|
|
5
|
+
Developer experience has been in freefall since the late 1990s.
|
|
6
|
+
|
|
7
|
+
In the REALbasic era, you could build an app, package it, and distribute it in a day. With PHP/LAMP, you could download a project, copy it to a server, edit a config file, and have a working app in an hour.
|
|
8
|
+
|
|
9
|
+
Then Node.js became the "standard stack" and standing up "hello world" became a nightmare of tooling, configuration, and dependencies. Platforms like Heroku helped, but we're still miles behind where we were with WordPress - let alone RAD tools from the 90s.
|
|
10
|
+
|
|
11
|
+
**tosijs-platform brings back that simplicity** - but with a modern stack.
|
|
12
|
+
|
|
13
|
+
## The Solution
|
|
14
|
+
|
|
15
|
+
1. Set up a Google developer account
|
|
16
|
+
2. Run `npx create-tosijs-platform-app my-site`
|
|
17
|
+
3. Follow the prompts
|
|
18
|
+
4. You have a working, production-ready website
|
|
19
|
+
|
|
20
|
+
The database is configured. Permissions are set up. Authentication works. You can use it as-is, or customize it *without ever deploying code* - using the `/esm` endpoint and `<tosi-esm>` component to load and run modules dynamically.
|
|
21
|
+
|
|
22
|
+
**tosijs-platform** is built on [tosijs](https://tosijs.net), which distills 30 years of UI development lessons into one small library. It eliminates the need for most state management and binding code (typically 75%+ of a React app) through automatic binding conventions - the same patterns that made 90s RAD tools so productive - while keeping your business logic free of framework dependencies.
|
|
23
|
+
|
|
24
|
+
## What You Get
|
|
25
|
+
|
|
26
|
+
- **Firebase backend** - Firestore, Auth, Storage, Cloud Functions
|
|
27
|
+
- **Built-in CMS** - blog, pages, custom content types
|
|
28
|
+
- **Fine-grained access control** - RBAC with field-level permissions
|
|
29
|
+
- **SEO-friendly SSR** - server-side rendering with prefetch
|
|
30
|
+
- **Type-safe** - TypeScript throughout
|
|
31
|
+
- **Extend without deploying** - dynamic ES modules via `/esm` endpoint
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx create-tosijs-platform-app my-awesome-site
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The CLI will:
|
|
40
|
+
1. ✓ Check Firebase CLI is installed and you're logged in
|
|
41
|
+
2. ✓ Ask for your Firebase project ID and admin email
|
|
42
|
+
3. ✓ Clone and configure the template
|
|
43
|
+
4. ✓ Install dependencies
|
|
44
|
+
5. ✓ Generate setup scripts
|
|
45
|
+
|
|
46
|
+
Then follow the printed instructions to deploy!
|
|
47
|
+
|
|
48
|
+
## Architecture: TypeScript Access Control
|
|
49
|
+
|
|
50
|
+
**tosijs-platform** uses a fundamentally different security model than typical Firebase apps:
|
|
51
|
+
|
|
52
|
+
| Traditional Firebase | tosijs-platform |
|
|
53
|
+
|---------------------|-----------------|
|
|
54
|
+
| Security rules in Google's DSL | Access control in TypeScript |
|
|
55
|
+
| Limited to document/collection level | **Field-level granularity** |
|
|
56
|
+
| Basic auth checks | **Full RBAC with 6 roles** |
|
|
57
|
+
| Rules separate from app logic | Access logic alongside validation |
|
|
58
|
+
| Hard to test | **Fully unit-testable** |
|
|
59
|
+
|
|
60
|
+
**How it works:**
|
|
61
|
+
- All data access goes through Cloud Functions (`/doc`, `/docs` endpoints)
|
|
62
|
+
- Access control is defined in TypeScript per-collection (see `functions/src/collections/`)
|
|
63
|
+
- Roles: `public` → `author` → `editor` → `admin` → `developer` → `owner`
|
|
64
|
+
- Each role can have different read/write/list permissions, down to individual fields
|
|
65
|
+
- Server-side validation with `tosijs-schema`
|
|
66
|
+
|
|
67
|
+
**Example access configuration:**
|
|
68
|
+
```typescript
|
|
69
|
+
access: {
|
|
70
|
+
[ROLES.public]: {
|
|
71
|
+
read: ALL, // Anyone can read
|
|
72
|
+
list: ALL, // Anyone can list
|
|
73
|
+
},
|
|
74
|
+
[ROLES.author]: {
|
|
75
|
+
write: ['title', 'body', 'tags'], // Authors can edit these fields
|
|
76
|
+
},
|
|
77
|
+
[ROLES.admin]: {
|
|
78
|
+
write: ALL, // Admins can edit everything
|
|
79
|
+
delete: true, // Admins can delete
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> **Important**: Cloud Functions deployment is **required** for the platform to work. The Firestore rules file (`firestore.rules`) uses deny-all defaults because all access is mediated through the Functions layer.
|
|
85
|
+
|
|
86
|
+
See [Firestore REST API & Security](docs/FIRESTORE_API.md) for complete documentation.
|
|
87
|
+
|
|
88
|
+
## Documentation
|
|
89
|
+
|
|
90
|
+
📚 **Core Concepts:**
|
|
91
|
+
- [Firestore REST API & Security](docs/FIRESTORE_API.md) - `/doc`, `/docs` endpoints and role-based access
|
|
92
|
+
- [ES Modules](docs/ESM_MODULES.md) - `/esm` endpoint and `<tosi-esm>` component for dynamic code loading
|
|
93
|
+
- [Prefetch & SEO](docs/PREFETCH.md) - Server-side rendering for fast loads and search engines
|
|
94
|
+
|
|
95
|
+
🧩 **Components:**
|
|
96
|
+
- [Blog Component](docs/BLOG_COMPONENT.md) - Full-featured blog with Markdown editing
|
|
97
|
+
- [Page Component](docs/PAGE_COMPONENT.md) - Generic HTML/component renderer
|
|
98
|
+
|
|
99
|
+
## Features
|
|
100
|
+
|
|
101
|
+
### Content Management
|
|
102
|
+
- **Built-in blog** with Markdown/HTML editing
|
|
103
|
+
- **Static pages** for about, contact, etc.
|
|
104
|
+
- **Media library** with image uploads to Cloud Storage
|
|
105
|
+
- **Easy to extend** - add custom content types by defining collections
|
|
106
|
+
|
|
107
|
+
### Developer Experience
|
|
108
|
+
- **Hot reload** dev server with HTTPS (uses self-signed TLS certs)
|
|
109
|
+
- **Type-safe** APIs and components
|
|
110
|
+
- **REST-based** data access (no SDK lock-in)
|
|
111
|
+
- **Flexible development** - work against production Firebase or use emulators
|
|
112
|
+
|
|
113
|
+
### Security & Access Control
|
|
114
|
+
- **Role-based access** (public, author, editor, admin, developer, owner)
|
|
115
|
+
- **Per-collection rules** with validation
|
|
116
|
+
- **Field-level permissions**
|
|
117
|
+
- **Server-side validation**
|
|
118
|
+
|
|
119
|
+
### Performance
|
|
120
|
+
- **Client-side rendering** with prefetch for SEO
|
|
121
|
+
- **Caching** (configurable per content type)
|
|
122
|
+
- **Optimized builds** with Bun
|
|
123
|
+
- **CDN-ready** static hosting
|
|
124
|
+
|
|
125
|
+
## Tech Stack
|
|
126
|
+
|
|
127
|
+
- **Frontend**: [tosijs](https://tosijs.net) + [tosijs-ui](https://ui.tosijs.net)
|
|
128
|
+
- **Build**: [Bun](https://bun.sh/) for lightning-fast builds
|
|
129
|
+
- **Backend**: [Firebase](https://firebase.google.com/) (Functions, Firestore, Auth, Storage)
|
|
130
|
+
- **Language**: TypeScript throughout
|
|
131
|
+
|
|
132
|
+
## Why Blaze Plan is Required
|
|
133
|
+
|
|
134
|
+
tosijs-platform uses **Cloud Functions** to provide a secure REST API for Firestore access. This approach:
|
|
135
|
+
- ✅ **Minimizes client bundle size** (no Firestore SDK in browser)
|
|
136
|
+
- ✅ **Centralized security** (validation and access control on server)
|
|
137
|
+
- ✅ **Fine-grained permissions** (role-based access, field-level filtering)
|
|
138
|
+
|
|
139
|
+
However, Cloud Functions are **only available on Firebase Blaze plan** (pay-as-you-go).
|
|
140
|
+
|
|
141
|
+
**Good news:** Blaze plan includes a generous free tier:
|
|
142
|
+
- 2M function invocations/month
|
|
143
|
+
- 5GB storage
|
|
144
|
+
- 10GB hosting transfer
|
|
145
|
+
|
|
146
|
+
Most small-to-medium sites stay **completely free** within these limits. You only pay for what you use beyond the free tier.
|
|
147
|
+
|
|
148
|
+
## Prerequisites
|
|
149
|
+
|
|
150
|
+
1. **Bun** - Install from [bun.sh](https://bun.sh)
|
|
151
|
+
```bash
|
|
152
|
+
curl -fsSL https://bun.sh/install | bash
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
2. **Firebase CLI** - For deployment
|
|
156
|
+
```bash
|
|
157
|
+
npm install -g firebase-tools
|
|
158
|
+
firebase login
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
3. **Firebase Project with Blaze Plan** - Create at [console.firebase.google.com](https://console.firebase.google.com)
|
|
162
|
+
- **REQUIRED: Blaze plan** (pay-as-you-go)
|
|
163
|
+
- tosijs-platform uses Cloud Functions for secure REST API access
|
|
164
|
+
- Free tier does NOT support Cloud Functions - the platform will not work without Blaze
|
|
165
|
+
- Blaze includes generous free tier: 2M function invocations/month, 5GB storage
|
|
166
|
+
- Most small sites stay within free limits
|
|
167
|
+
- Note your **Project ID**
|
|
168
|
+
|
|
169
|
+
## Installation & Setup
|
|
170
|
+
|
|
171
|
+
### Step 1: Create Project
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npx create-tosijs-platform-app my-site
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
You'll be prompted for:
|
|
178
|
+
- **Firebase Project ID**: Your Firebase project ID (from console)
|
|
179
|
+
- **Admin Email**: Your Google account email (for owner access)
|
|
180
|
+
- **Site Name**: Display name for your site
|
|
181
|
+
- **Site Description**: Meta description
|
|
182
|
+
|
|
183
|
+
### Step 2: Get Firebase Config
|
|
184
|
+
|
|
185
|
+
1. Go to [Firebase Console](https://console.firebase.google.com)
|
|
186
|
+
2. Select your project
|
|
187
|
+
3. Go to **Project Settings** → **General**
|
|
188
|
+
4. Scroll to **Your apps** → Add a web app (or use existing)
|
|
189
|
+
5. Copy the config object
|
|
190
|
+
|
|
191
|
+
### Step 3: Update Configuration
|
|
192
|
+
|
|
193
|
+
Edit `src/firebase-config.ts` with your Firebase config:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
const PROJECT_ID = 'your-project-id'
|
|
197
|
+
|
|
198
|
+
export const config = {
|
|
199
|
+
authDomain: `${PROJECT_ID}.firebaseapp.com`,
|
|
200
|
+
projectId: PROJECT_ID,
|
|
201
|
+
storageBucket: `${PROJECT_ID}.appspot.com`,
|
|
202
|
+
apiKey: 'YOUR_API_KEY',
|
|
203
|
+
messagingSenderId: 'YOUR_SENDER_ID',
|
|
204
|
+
appId: 'YOUR_APP_ID',
|
|
205
|
+
measurementId: 'YOUR_MEASUREMENT_ID',
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Step 4: Enable Firebase Services
|
|
210
|
+
|
|
211
|
+
In Firebase Console, enable:
|
|
212
|
+
|
|
213
|
+
1. **Authentication** → Sign-in method → Google (enable)
|
|
214
|
+
2. **Firestore Database** → Create database (production mode)
|
|
215
|
+
3. **Storage** → Get started
|
|
216
|
+
4. **Functions** → (automatically enabled with Blaze plan)
|
|
217
|
+
|
|
218
|
+
### Step 5: Deploy Functions
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
cd my-site
|
|
222
|
+
bun deploy-functions
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Wait for deployment to complete (~2-5 minutes).
|
|
226
|
+
|
|
227
|
+
### Step 6: Initialize Admin User
|
|
228
|
+
|
|
229
|
+
After functions are deployed, run the setup script:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
bun setup.js
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
This creates:
|
|
236
|
+
- Owner role for your admin email
|
|
237
|
+
- Welcome post to get started
|
|
238
|
+
|
|
239
|
+
### Step 7: Start Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
bun start
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Visit **https://localhost:8020** and sign in with your admin email.
|
|
246
|
+
|
|
247
|
+
> **Note:** Your browser will warn about the self-signed certificate - this is expected. Click through to proceed.
|
|
248
|
+
|
|
249
|
+
#### Why HTTPS for Local Development?
|
|
250
|
+
|
|
251
|
+
Unlike typical Firebase setups that use HTTP emulators, tosijs-platform uses a custom HTTPS dev server that connects directly to your production Firebase backend. This approach:
|
|
252
|
+
|
|
253
|
+
- **Simplifies development** - no emulator setup or management
|
|
254
|
+
- **Matches production** - test against real data and auth
|
|
255
|
+
- **Enables secure cookies** - Firebase Auth requires HTTPS
|
|
256
|
+
- **Faster startup** - just `bun start`, no emulator spin-up
|
|
257
|
+
|
|
258
|
+
The TLS certificates in `tls/` are generated automatically by `create-tosijs-platform-app`, or you can regenerate them with `./tls/create-dev-certs.sh`.
|
|
259
|
+
|
|
260
|
+
### Step 8: Deploy Hosting
|
|
261
|
+
|
|
262
|
+
When ready to go live:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
bun deploy-hosting
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Your site will be live at `https://your-project-id.web.app`
|
|
269
|
+
|
|
270
|
+
## Project Structure
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
my-site/
|
|
274
|
+
├── src/ # Client-side code
|
|
275
|
+
│ ├── index.ts # App entry point
|
|
276
|
+
│ ├── app.ts # Global state
|
|
277
|
+
│ ├── blog.ts # Blog component
|
|
278
|
+
│ ├── firebase.ts # Firebase client wrapper
|
|
279
|
+
│ ├── tosi-esm.ts # Dynamic ES module loader component
|
|
280
|
+
│ └── style.ts # Theme & styling
|
|
281
|
+
├── functions/ # Cloud Functions
|
|
282
|
+
│ ├── src/
|
|
283
|
+
│ │ ├── index.ts # Function exports
|
|
284
|
+
│ │ ├── doc.ts # Document CRUD API
|
|
285
|
+
│ │ ├── docs.ts # Collection query API
|
|
286
|
+
│ │ ├── esm.ts # ES module serving endpoint
|
|
287
|
+
│ │ ├── gen.ts # LLM generation endpoint
|
|
288
|
+
│ │ ├── prefetch.ts # SSR prefetch endpoint
|
|
289
|
+
│ │ ├── blog.ts # Blog collection config
|
|
290
|
+
│ │ ├── module.ts # Module collection config
|
|
291
|
+
│ │ ├── access.ts # Access control system
|
|
292
|
+
│ │ ├── elements.ts # Server-side HTML rendering
|
|
293
|
+
│ │ └── roles.ts # Role definitions
|
|
294
|
+
│ └── shared/ # Shared TypeScript types
|
|
295
|
+
│ ├── module.ts # Module interface
|
|
296
|
+
│ └── page.ts # Page interface
|
|
297
|
+
├── initial_state/ # Seed data for Firestore
|
|
298
|
+
│ └── firestore/
|
|
299
|
+
│ ├── page.json # Initial pages
|
|
300
|
+
│ └── module.json # Initial modules
|
|
301
|
+
├── public/ # Static assets
|
|
302
|
+
│ ├── index.html
|
|
303
|
+
│ └── logo.svg
|
|
304
|
+
├── firebase.json # Firebase config
|
|
305
|
+
├── firestore.rules # Security rules
|
|
306
|
+
├── storage.rules # Storage security
|
|
307
|
+
└── dev.ts # Dev server
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Adding Custom Content Types
|
|
311
|
+
|
|
312
|
+
Define new content types by adding to `COLLECTIONS` in `functions/src/`:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// functions/src/products.ts
|
|
316
|
+
import { COLLECTIONS } from './collections'
|
|
317
|
+
import { ROLES } from './roles'
|
|
318
|
+
import { ALL } from './access'
|
|
319
|
+
|
|
320
|
+
COLLECTIONS.product = {
|
|
321
|
+
unique: ['sku'],
|
|
322
|
+
validate: async (data) => {
|
|
323
|
+
if (!data.name || !data.price) {
|
|
324
|
+
return new Error('Name and price required')
|
|
325
|
+
}
|
|
326
|
+
return data
|
|
327
|
+
},
|
|
328
|
+
access: {
|
|
329
|
+
[ROLES.public]: {
|
|
330
|
+
read: ALL,
|
|
331
|
+
list: ALL,
|
|
332
|
+
},
|
|
333
|
+
[ROLES.admin]: {
|
|
334
|
+
write: ALL,
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Then import in `functions/src/index.ts`:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
import './products'
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Customization
|
|
347
|
+
|
|
348
|
+
### Theme
|
|
349
|
+
|
|
350
|
+
Edit `src/style.ts` to customize colors, fonts, spacing:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
export const theme = tosi({
|
|
354
|
+
mode: 'light', // 'light' | 'dark' | 'system'
|
|
355
|
+
colors: {
|
|
356
|
+
primary: '#007acc',
|
|
357
|
+
// ... more colors
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Content Collections
|
|
363
|
+
|
|
364
|
+
Each content type (blog posts, pages, etc.) is defined in `functions/src/` as a collection config with:
|
|
365
|
+
- **Validation** rules
|
|
366
|
+
- **Unique** field constraints
|
|
367
|
+
- **Access control** per role
|
|
368
|
+
- **Field-level** permissions
|
|
369
|
+
|
|
370
|
+
See `functions/src/blog.ts` for a complete example.
|
|
371
|
+
|
|
372
|
+
### UI Components
|
|
373
|
+
|
|
374
|
+
Create custom components using tosijs:
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { elements, Component } from 'tosijs'
|
|
378
|
+
|
|
379
|
+
export class MyComponent extends Component {
|
|
380
|
+
content = () => {
|
|
381
|
+
const { div, h1 } = elements
|
|
382
|
+
return div(
|
|
383
|
+
h1('Hello World')
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Custom Endpoints
|
|
390
|
+
|
|
391
|
+
Create custom Cloud Function endpoints. See `functions/src/hello.ts` for a minimal example:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { onRequest } from 'firebase-functions/v2/https'
|
|
395
|
+
import compression from 'compression'
|
|
396
|
+
import { optionsResponse, getUserRoles } from './utilities'
|
|
397
|
+
|
|
398
|
+
const compressResponse = compression()
|
|
399
|
+
|
|
400
|
+
export const myEndpoint = onRequest({}, async (req, res) => {
|
|
401
|
+
// Handle CORS preflight
|
|
402
|
+
if (optionsResponse(req, res)) {
|
|
403
|
+
return
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Get authenticated user's roles
|
|
407
|
+
const userRoles = await getUserRoles(req)
|
|
408
|
+
|
|
409
|
+
// Your logic here
|
|
410
|
+
compressResponse(req, res, () => {
|
|
411
|
+
res.json({ message: 'Hello!', roles: userRoles.roles })
|
|
412
|
+
})
|
|
413
|
+
})
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Then export in `functions/src/index.ts`:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
export { myEndpoint } from './my-endpoint'
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
For endpoints using secrets (API keys), see `functions/src/gen.ts` which demonstrates the `defineSecret` pattern.
|
|
423
|
+
|
|
424
|
+
## Development Commands
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
bun start # Start dev server (https://localhost:8020)
|
|
428
|
+
bun start-emulated # Start with Firebase emulators
|
|
429
|
+
bun seed # Seed emulators with initial_state data
|
|
430
|
+
bun seed-clear # Clear emulators and reseed
|
|
431
|
+
bun deploy-functions # Deploy Cloud Functions
|
|
432
|
+
bun deploy-hosting # Deploy static hosting
|
|
433
|
+
bun format # Format code with Prettier
|
|
434
|
+
bun latest # Update all dependencies
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Using Emulators
|
|
438
|
+
|
|
439
|
+
For isolated development without affecting production data:
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
# Start emulators and dev server
|
|
443
|
+
bun start-emulated
|
|
444
|
+
|
|
445
|
+
# In another terminal, seed with initial data
|
|
446
|
+
bun seed
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
The emulators provide local Firestore, Auth, Storage, and Functions. Data is seeded from `initial_state/firestore/`.
|
|
450
|
+
|
|
451
|
+
## Secrets Management
|
|
452
|
+
|
|
453
|
+
For API keys (e.g., Gemini, OpenAI, Stripe), use Firebase Secret Manager (required for v2 functions):
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
firebase functions:secrets:set OPENAI_API_KEY
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Access in functions:
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { defineSecret } from 'firebase-functions/params'
|
|
463
|
+
|
|
464
|
+
const openaiKey = defineSecret('OPENAI_API_KEY')
|
|
465
|
+
|
|
466
|
+
export const myFunction = onRequest(
|
|
467
|
+
{ secrets: [openaiKey] },
|
|
468
|
+
async (req, res) => {
|
|
469
|
+
const key = openaiKey.value()
|
|
470
|
+
// use key...
|
|
471
|
+
}
|
|
472
|
+
)
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
## User Management
|
|
476
|
+
|
|
477
|
+
Users are managed via the `user` collection in Firestore. Roles are assigned per user:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// In Firestore console or via code:
|
|
481
|
+
collection('user').doc(userId).set({
|
|
482
|
+
email: 'user@example.com',
|
|
483
|
+
roles: ['author'], // 'public', 'author', 'editor', 'admin', 'developer', 'owner'
|
|
484
|
+
})
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Deployment Checklist
|
|
488
|
+
|
|
489
|
+
- [ ] Firebase config updated in `src/firebase-config.ts`
|
|
490
|
+
- [ ] Google Auth enabled in Firebase Console
|
|
491
|
+
- [ ] Firestore Database created
|
|
492
|
+
- [ ] Cloud Storage enabled
|
|
493
|
+
- [ ] Functions deployed (`bun deploy-functions`)
|
|
494
|
+
- [ ] Admin user created (`bun setup.js`)
|
|
495
|
+
- [ ] Local dev working (`bun start`)
|
|
496
|
+
- [ ] Hosting deployed (`bun deploy-hosting`)
|
|
497
|
+
- [ ] Custom domain configured (optional)
|
|
498
|
+
|
|
499
|
+
## Troubleshooting
|
|
500
|
+
|
|
501
|
+
### "Firebase CLI not found"
|
|
502
|
+
```bash
|
|
503
|
+
npm install -g firebase-tools
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### "Not logged in to Firebase"
|
|
507
|
+
```bash
|
|
508
|
+
firebase login
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### "Functions deployment failed"
|
|
512
|
+
- Check Firebase project has Blaze plan enabled
|
|
513
|
+
- Verify `.firebaserc` has correct project ID
|
|
514
|
+
|
|
515
|
+
### "Permission denied" errors
|
|
516
|
+
- Make sure admin user exists in `user` collection
|
|
517
|
+
- Check `roles` array includes 'owner' or 'admin'
|
|
518
|
+
|
|
519
|
+
### "CORS errors" in development
|
|
520
|
+
- The dev server uses HTTPS (required for Firebase)
|
|
521
|
+
- Accept the self-signed certificate in your browser
|
|
522
|
+
|
|
523
|
+
## Performance Tips
|
|
524
|
+
|
|
525
|
+
1. **Limit Firestore reads**: Use caching and prefetch
|
|
526
|
+
2. **Optimize images**: Compress before uploading
|
|
527
|
+
3. **Use Cloud CDN**: Firebase Hosting includes CDN
|
|
528
|
+
4. **Monitor costs**: Check Firebase usage dashboard
|
|
529
|
+
|
|
530
|
+
## Contributing
|
|
531
|
+
|
|
532
|
+
Issues and PRs welcome at [github.com/tonioloewald/tosijs-platform](https://github.com/tonioloewald/tosijs-platform)
|
|
533
|
+
|
|
534
|
+
## License
|
|
535
|
+
|
|
536
|
+
MIT © Tonio Loewald
|
|
537
|
+
|
|
538
|
+
## Learn More
|
|
539
|
+
|
|
540
|
+
- [tosijs Documentation](https://tosijs.net) - State management and components (includes AI context)
|
|
541
|
+
- [tosijs-ui Components](https://ui.tosijs.net) - UI component library with live examples
|
|
542
|
+
- [Firebase Documentation](https://firebase.google.com/docs)
|
|
543
|
+
- [Bun Documentation](https://bun.sh/docs)
|
|
544
|
+
|
|
545
|
+
## Platform Documentation
|
|
546
|
+
|
|
547
|
+
- **[Firestore REST API & Security](docs/FIRESTORE_API.md)** - How the REST endpoints work, access control, validation
|
|
548
|
+
- **[ES Modules](docs/ESM_MODULES.md)** - Dynamic code loading via `/esm` endpoint
|
|
549
|
+
- **[LLM Generation](docs/GEN_ENDPOINT.md)** - `/gen` endpoint for Gemini/ChatGPT text generation
|
|
550
|
+
- **[Blog Component](docs/BLOG_COMPONENT.md)** - Built-in blog system, editing, publishing
|
|
551
|
+
- **[Page Component](docs/PAGE_COMPONENT.md)** - Generic content renderer, static pages
|
|
552
|
+
- **[Prefetch & SEO](docs/PREFETCH.md)** - Server-side rendering, meta tags, social media previews
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tosijs-platform",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Full-stack web application platform - Firebase + tosijs, simpler than PHP/LAMP",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"tosijs",
|
|
7
|
+
"firebase",
|
|
8
|
+
"cms",
|
|
9
|
+
"blog",
|
|
10
|
+
"platform",
|
|
11
|
+
"full-stack"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/tonioloewald/tosijs-platform",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/tonioloewald/tosijs-platform.git"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Tonio Loewald",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"files": [
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"start": "bun --watch dev.ts",
|
|
27
|
+
"kill-ports": "lsof -ti:4000,5001,5002,8080,9099,9199 | xargs kill -9 2>/dev/null || true",
|
|
28
|
+
"start-emulated": "bun run kill-ports && bun run build && (cd functions && bun run build) && bun bin/start-emulated.js",
|
|
29
|
+
"seed": "bun bin/seed-emulators.js",
|
|
30
|
+
"seed-clear": "bun bin/seed-emulators.js --clear",
|
|
31
|
+
"build": "bun build ./src/index.ts --outdir ./dist --sourcemap=linked --minify && cp -a public/. dist/ && rm dist/index.html",
|
|
32
|
+
"format": "bun prettier --write src functions/src",
|
|
33
|
+
"latest": "bun upgrade && bun install && cd functions && npm install && cd ..",
|
|
34
|
+
"deploy-hosting": "firebase deploy --only hosting && echo 'deployed hosting'",
|
|
35
|
+
"deploy-functions": "firebase deploy --only functions && echo 'deployed functions'"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/bun": "^1.3.5",
|
|
39
|
+
"@types/marked": "^5.0.2",
|
|
40
|
+
"@types/turndown": "^5.0.6",
|
|
41
|
+
"@types/web": "^0.0.99",
|
|
42
|
+
"bun-types": "canary",
|
|
43
|
+
"chokidar": "^3.6.0",
|
|
44
|
+
"http-server": "^14.1.1",
|
|
45
|
+
"marked": "^16.4.2",
|
|
46
|
+
"prettier": "^2.8.8",
|
|
47
|
+
"tosijs": "^1.0.10",
|
|
48
|
+
"tosijs-schema": "^1.0.3",
|
|
49
|
+
"tosijs-ui": "^1.0.10",
|
|
50
|
+
"typescript": "^5.9.3"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"firebase": "^12.7.0",
|
|
54
|
+
"turndown": "^7.2.2"
|
|
55
|
+
}
|
|
56
|
+
}
|