easy-starter 1.2.0 → 2.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/README.md +32 -80
- package/bin/index.js +219 -44
- package/package.json +20 -11
- package/template/.cursorrules +23 -0
- package/template/.github/workflows/deploy.yml +3 -0
- package/template/README.md +81 -24
- package/template/env +3 -2
- package/template/gitignore +1 -1
- package/template/index.html +15 -0
- package/template/package.json +8 -9
- package/template/public/images/icon-16.png +0 -0
- package/template/public/images/icon-180.png +0 -0
- package/template/public/images/icon-192.png +0 -0
- package/template/public/images/icon-32.png +0 -0
- package/template/public/images/icon-512.png +0 -0
- package/template/{src → public}/site.webmanifest +6 -12
- package/template/scripts/deploy.ts +3 -2
- package/template/server/index.tsx +248 -61
- package/template/server/mail.tsx +120 -0
- package/template/src/App.tsx +33 -7
- package/template/src/Router.tsx +11 -2
- package/template/src/index.tsx +28 -1
- package/template/src/pages/Index.tsx +45 -6
- package/template/src/pages/ResetPassword.tsx +126 -0
- package/template/src/pages/admin/Admin.tsx +360 -85
- package/template/src/pages/admin/AnalyticsTab.tsx +71 -0
- package/template/src/pages/admin/ErrorsTab.tsx +46 -0
- package/template/src/pages/admin/UsersTab.tsx +61 -0
- package/template/src/services/api.ts +5 -7
- package/template/src/services/common.ts +23 -0
- package/template/src/styles.css +460 -0
- package/template/tsconfig.json +2 -1
- package/template/types.ts +44 -3
- package/template/vite.config.ts +24 -0
- package/template/.parcelrc +0 -6
- package/template/src/images/icon.png +0 -0
- package/template/src/index.html +0 -16
- package/template/src/styles.scss +0 -292
package/template/README.md
CHANGED
|
@@ -4,23 +4,54 @@ This project was bootstrapped with `easy-starter`, giving you maximum freedom wi
|
|
|
4
4
|
|
|
5
5
|
## Features Included
|
|
6
6
|
|
|
7
|
-
* **React 19 & TypeScript**:
|
|
7
|
+
* **React 19 & TypeScript 7.0**: A modern, state-of-the-art type-safe frontend experience.
|
|
8
8
|
* **Express Backend**: A simple Node.js server.
|
|
9
|
-
* **
|
|
10
|
-
* **
|
|
11
|
-
* **
|
|
12
|
-
* **Local Database**: Ready to use with [`easy-db-node`](https://www.npmjs.com/package/easy-db-node).
|
|
9
|
+
* **Vite v8 Bundler**: Zero-config client compilation with lightning-fast HMR and proxying.
|
|
10
|
+
* **Type-Safe API**: RPC communication via [`typed-client-server-api`](https://www.npmjs.com/package/typed-client-server-api).
|
|
11
|
+
* **Local Database**: local JSON-based document storage with [`easy-db-node`](https://www.npmjs.com/package/easy-db-node).
|
|
13
12
|
* **Routing**: Managed by [`easy-page-router`](https://www.npmjs.com/package/easy-page-router).
|
|
13
|
+
<!-- --- AUTH START --- -->
|
|
14
|
+
* **User Authentication**: Pre-configured user registration, login dialogs, cookie rotation, and password reset flows via [`easy-user-auth`](https://www.npmjs.com/package/easy-user-auth).
|
|
15
|
+
<!-- --- AUTH END --- -->
|
|
16
|
+
<!-- --- SSR START --- -->
|
|
17
|
+
* **SSR Ready**: Server-side rendering and hydration powered by [`ssr-hook`](https://www.npmjs.com/package/ssr-hook).
|
|
18
|
+
<!-- --- SSR END --- -->
|
|
19
|
+
<!-- --- ANALYTICS START --- -->
|
|
20
|
+
* **Analytics**: Privacy-friendly local analytics tracked via [`easy-analytics`](https://www.npmjs.com/package/easy-analytics).
|
|
21
|
+
<!-- --- ANALYTICS END --- -->
|
|
22
|
+
<!-- --- OGIMAGE START --- -->
|
|
23
|
+
* **og:image Generation**: Dynamic page screenshotting for social previews using headless `puppeteer`.
|
|
24
|
+
<!-- --- OGIMAGE END --- -->
|
|
25
|
+
|
|
26
|
+
---
|
|
14
27
|
|
|
15
28
|
## Getting Started
|
|
16
29
|
|
|
17
|
-
To start developing
|
|
30
|
+
To start developing:
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
32
|
+
1. Install dependencies (already completed by easy-starter):
|
|
33
|
+
```bash
|
|
34
|
+
npm install
|
|
35
|
+
```
|
|
36
|
+
<!-- --- AUTH START --- -->
|
|
37
|
+
2. **Configure SMTP settings:**
|
|
38
|
+
Open the generated `.env` file and fill in your SMTP credentials. This is required for sending password recovery links to users:
|
|
39
|
+
```env
|
|
40
|
+
MAIL_SMTP=smtp.example.com
|
|
41
|
+
MAIL_PORT=587
|
|
42
|
+
MAIL_USER=your-email@example.com
|
|
43
|
+
MAIL_PASSWORD=your-email-password
|
|
44
|
+
```
|
|
45
|
+
<!-- --- AUTH END --- -->
|
|
46
|
+
|
|
47
|
+
3. Run the development environment:
|
|
48
|
+
```bash
|
|
49
|
+
npm run dev
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This concurrently starts your Express server (port `1111`) and the Vite bundler (port `5173`). Open [http://localhost:5173](http://localhost:5173) in your browser. The page will automatically reload when you make changes.
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
---
|
|
24
55
|
|
|
25
56
|
## Core Concepts & Examples
|
|
26
57
|
|
|
@@ -28,10 +59,10 @@ This will concurrently start your Express server and the Parcel bundler. Open [h
|
|
|
28
59
|
|
|
29
60
|
### 1. Defining your API
|
|
30
61
|
|
|
31
|
-
Your API contract is defined in
|
|
62
|
+
Your API contract is defined in `types.ts`. This ensures your frontend and backend are always perfectly in sync.
|
|
32
63
|
|
|
33
64
|
```typescript
|
|
34
|
-
// types.
|
|
65
|
+
// types.ts
|
|
35
66
|
import { APIDefinition, Endpoint } from "typed-client-server-api";
|
|
36
67
|
|
|
37
68
|
export type API = APIDefinition<{
|
|
@@ -45,21 +76,21 @@ On the server, you implement these endpoints:
|
|
|
45
76
|
|
|
46
77
|
```typescript
|
|
47
78
|
// server/index.tsx
|
|
48
|
-
import {
|
|
79
|
+
import { setAPIBackend } from "typed-client-server-api";
|
|
49
80
|
import { API } from "../types";
|
|
50
81
|
|
|
51
|
-
|
|
82
|
+
setAPIBackend<API>(app, {
|
|
52
83
|
getItems: async () => {
|
|
53
|
-
return
|
|
84
|
+
return selectArray("item");
|
|
54
85
|
},
|
|
55
86
|
addItem: async ({ name, value }) => {
|
|
56
|
-
return
|
|
87
|
+
return insert("item", { name, value });
|
|
57
88
|
}
|
|
58
89
|
});
|
|
59
90
|
```
|
|
60
91
|
|
|
61
|
-
### 2. Fetching Data
|
|
62
|
-
|
|
92
|
+
### 2. Fetching Data
|
|
93
|
+
<!-- --- SSR START --- -->
|
|
63
94
|
To fetch data on the server during SSR and seamlessly hydrate it on the client without loading spinners, use `useSSRApi` in your React components.
|
|
64
95
|
|
|
65
96
|
```tsx
|
|
@@ -68,8 +99,6 @@ import React from "react";
|
|
|
68
99
|
import { useSSRApi } from "../services/api";
|
|
69
100
|
|
|
70
101
|
export default function Index() {
|
|
71
|
-
// This will run on the server, fetch the data, embed it in the HTML,
|
|
72
|
-
// and hydrate perfectly on the client. Fully type-safe!
|
|
73
102
|
const [items, error, isLoading, reload] = useSSRApi.getItems({});
|
|
74
103
|
|
|
75
104
|
if (isLoading) return <p>Loading...</p>;
|
|
@@ -82,25 +111,53 @@ export default function Index() {
|
|
|
82
111
|
);
|
|
83
112
|
}
|
|
84
113
|
```
|
|
114
|
+
<!-- --- SSR END --- -->
|
|
115
|
+
<!-- --- NO_SSR START --- -->
|
|
116
|
+
To fetch data on the client reactively, use `useApi` in your React components.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// src/pages/Index.tsx
|
|
120
|
+
import React from "react";
|
|
121
|
+
import { useApi } from "../services/api";
|
|
122
|
+
|
|
123
|
+
export default function Index() {
|
|
124
|
+
const [items, error, isLoading, reload] = useApi.getItems({});
|
|
125
|
+
|
|
126
|
+
if (isLoading) return <p>Loading...</p>;
|
|
127
|
+
if (error) return <p>Error: {error.message}</p>;
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<ul>
|
|
131
|
+
{items?.map(item => <li key={item._id}>{item.name}</li>)}
|
|
132
|
+
</ul>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
<!-- --- NO_SSR END --- -->
|
|
85
137
|
|
|
86
138
|
### 3. AI Integration (`.cursorrules`)
|
|
87
139
|
|
|
88
|
-
This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter
|
|
140
|
+
This template includes a pre-configured `.cursorrules` file in the root. If you develop using AI assistants such as Cursor, Windsurf, or GitHub Copilot, they will automatically read this file to understand the specific conventions of `easy-starter`.
|
|
141
|
+
|
|
142
|
+
---
|
|
89
143
|
|
|
90
144
|
## Scripts
|
|
91
145
|
|
|
92
|
-
* `npm run dev` - Starts
|
|
146
|
+
* `npm run dev` - Starts backend and Vite dev server.
|
|
93
147
|
* `npm run build` - Builds the application for production.
|
|
94
148
|
* `npm start` - Starts the production server (run build first).
|
|
95
149
|
* `npm run deploy` - (If included) Deploys your app to a remote server via SSH.
|
|
96
150
|
|
|
151
|
+
---
|
|
152
|
+
|
|
97
153
|
## Project Structure
|
|
98
154
|
|
|
99
155
|
* `/src` - Your React frontend code.
|
|
100
156
|
* `/server` - Your Express backend code.
|
|
101
157
|
* `/types.ts` - Shared TypeScript definitions for your API and Database.
|
|
102
158
|
* `/.cursorrules` - Rules instructing AI coding assistants how to interact with the project stack.
|
|
103
|
-
* `/public` - Static assets (images,
|
|
104
|
-
* `/easy-db` - Where
|
|
159
|
+
* `/public` - Static assets served at the root (images, favicon, site.webmanifest).
|
|
160
|
+
* `/easy-db-files` - Where uploaded database files are served.
|
|
161
|
+
* `/easy-db-backup` - Automated backup folder for your JSON collections.
|
|
105
162
|
|
|
106
163
|
Enjoy building with freedom!
|
package/template/env
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
NODE_ENV=development # development/production
|
|
2
2
|
SERVER_PORT=1111
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
SERVER_URL=https://your-production-url.com/
|
|
4
5
|
|
|
5
6
|
# deploy
|
|
6
7
|
SSH_HOST=[IP_ADDRESS]
|
|
7
8
|
SSH_USERNAME=root
|
|
8
9
|
SSH_PASSWORD=[PASSWORD]
|
|
9
|
-
|
|
10
|
+
SSH_PATH=/root/easy-starter-app
|
|
10
11
|
PM2_APP_NAME=easy-starter-app
|
package/template/gitignore
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<link rel="apple-touch-icon" sizes="180x180" href="/images/icon-180.png" />
|
|
7
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/images/icon-32.png" />
|
|
8
|
+
<link rel="icon" type="image/png" sizes="16x16" href="/images/icon-16.png" />
|
|
9
|
+
<link rel="manifest" href="/site.webmanifest" />
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
<script type="module" src="/src/index.tsx"></script>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
package/template/package.json
CHANGED
|
@@ -6,30 +6,29 @@
|
|
|
6
6
|
"start": "tsx --env-file=.env server/index.tsx",
|
|
7
7
|
"dev": "concurrently --kill-others \"npm run server:dev\" \"npm run fe:dev\"",
|
|
8
8
|
"server:dev": "tsx --env-file=.env --watch server/index.tsx",
|
|
9
|
-
"fe:dev": "
|
|
10
|
-
"build": "
|
|
11
|
-
"deploy": "tsx scripts/deploy.ts"
|
|
9
|
+
"fe:dev": "vite",
|
|
10
|
+
"build": "vite build"
|
|
12
11
|
},
|
|
13
12
|
"dependencies": {
|
|
14
13
|
"cors": "^2.8.6",
|
|
15
|
-
"easy-analytics": "^1.0.1",
|
|
16
14
|
"easy-db-node": "^3.0.1",
|
|
17
15
|
"easy-page-router": "^0.2.1",
|
|
18
16
|
"express": "^5.2.1",
|
|
19
17
|
"react": "^19.2.6",
|
|
20
18
|
"react-dom": "^19.2.6",
|
|
21
|
-
"ssr-hook": "^0.2.0",
|
|
22
19
|
"typed-client-server-api": "^1.1.0"
|
|
23
20
|
},
|
|
24
21
|
"devDependencies": {
|
|
25
22
|
"@types/cors": "^2.8.19",
|
|
26
23
|
"@types/express": "^5.0.6",
|
|
27
24
|
"@types/node": "^24.0.0",
|
|
28
|
-
"@types/react": "^19.
|
|
29
|
-
"@types/react-dom": "^19.
|
|
25
|
+
"@types/react": "^19.0.0",
|
|
26
|
+
"@types/react-dom": "^19.0.0",
|
|
30
27
|
"concurrently": "^9.2.1",
|
|
31
|
-
"parcel": "^2.16.4",
|
|
32
28
|
"tsx": "^4.21.0",
|
|
33
|
-
"typescript": "^
|
|
29
|
+
"typescript": "^7.0.0",
|
|
30
|
+
"vite": "^8.1.5",
|
|
31
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
32
|
+
"vite-imagetools": "^10.0.1"
|
|
34
33
|
}
|
|
35
34
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -8,39 +8,33 @@
|
|
|
8
8
|
"background_color": "#09090b",
|
|
9
9
|
"icons": [
|
|
10
10
|
{
|
|
11
|
-
"src": "
|
|
11
|
+
"src": "/images/icon-16.png",
|
|
12
12
|
"sizes": "16x16",
|
|
13
13
|
"type": "image/png",
|
|
14
14
|
"purpose": "maskable"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
"src": "
|
|
17
|
+
"src": "/images/icon-32.png",
|
|
18
18
|
"sizes": "32x32",
|
|
19
19
|
"type": "image/png",
|
|
20
20
|
"purpose": "maskable"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
"src": "
|
|
23
|
+
"src": "/images/icon-180.png",
|
|
24
24
|
"sizes": "180x180",
|
|
25
25
|
"type": "image/png",
|
|
26
26
|
"purpose": "maskable"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
|
-
"src": "
|
|
30
|
-
"sizes": "192x192",
|
|
31
|
-
"type": "image/webp",
|
|
32
|
-
"purpose": "maskable"
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
"src": "./images/icon.png?width=192&height=192",
|
|
29
|
+
"src": "/images/icon-192.png",
|
|
36
30
|
"sizes": "192x192",
|
|
37
31
|
"type": "image/png",
|
|
38
32
|
"purpose": "maskable"
|
|
39
33
|
},
|
|
40
34
|
{
|
|
41
|
-
"src": "
|
|
35
|
+
"src": "/images/icon-512.png",
|
|
42
36
|
"sizes": "512x512",
|
|
43
|
-
"type": "image/
|
|
37
|
+
"type": "image/png",
|
|
44
38
|
"purpose": "maskable"
|
|
45
39
|
}
|
|
46
40
|
]
|
|
@@ -11,7 +11,7 @@ const password = process.env.SSH_PASSWORD;
|
|
|
11
11
|
// Configuration for your target server
|
|
12
12
|
const server = process.env.SERVER_URL;
|
|
13
13
|
const pm2AppName = process.env.PM2_APP_NAME;
|
|
14
|
-
const sshPath =
|
|
14
|
+
const sshPath = process.env.SSH_PATH; // Path on the remote VPS
|
|
15
15
|
|
|
16
16
|
const path = resolve(process.cwd()); // Local project path
|
|
17
17
|
const ignoreFiles = [
|
|
@@ -26,10 +26,11 @@ const ignoreFiles = [
|
|
|
26
26
|
".DS_Store",
|
|
27
27
|
];
|
|
28
28
|
|
|
29
|
-
// Ensure credentials are provided
|
|
29
|
+
// Ensure credentials and paths are provided
|
|
30
30
|
if (host === undefined) throw new Error("SSH_HOST environment variable is not defined.");
|
|
31
31
|
if (username === undefined) throw new Error("SSH_USERNAME environment variable is not defined.");
|
|
32
32
|
if (password === undefined) throw new Error("SSH_PASSWORD environment variable is not defined.");
|
|
33
|
+
if (sshPath === undefined) throw new Error("SSH_PATH environment variable is not defined.");
|
|
33
34
|
|
|
34
35
|
deploy();
|
|
35
36
|
|