create-pxlr 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/README.md +160 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +264 -0
- package/package.json +51 -0
- package/templates/blog/frontend/app/blog/[slug]/page.tsx +175 -0
- package/templates/blog/frontend/app/blog/page.tsx +102 -0
- package/templates/blog/frontend/app/components/footer.tsx +21 -0
- package/templates/blog/frontend/app/components/header.tsx +45 -0
- package/templates/blog/frontend/app/globals.css +30 -0
- package/templates/blog/frontend/app/layout.tsx +38 -0
- package/templates/blog/frontend/app/lib/cms.ts +71 -0
- package/templates/blog/frontend/app/page.tsx +155 -0
- package/templates/blog/frontend/next.config.ts +16 -0
- package/templates/blog/frontend/package.json +24 -0
- package/templates/blog/frontend/postcss.config.mjs +7 -0
- package/templates/blog/frontend/tsconfig.json +23 -0
- package/templates/blog/pxlr-cms/README.md +188 -0
- package/templates/blog/pxlr-cms/docker-compose.yml +132 -0
- package/templates/blog/pxlr-cms/nginx/nginx.conf +107 -0
- package/templates/blog/pxlr-cms/packages/admin/.dockerignore +4 -0
- package/templates/blog/pxlr-cms/packages/admin/.env.example +2 -0
- package/templates/blog/pxlr-cms/packages/admin/Dockerfile +19 -0
- package/templates/blog/pxlr-cms/packages/admin/next-env.d.ts +6 -0
- package/templates/blog/pxlr-cms/packages/admin/next.config.ts +22 -0
- package/templates/blog/pxlr-cms/packages/admin/package.json +63 -0
- package/templates/blog/pxlr-cms/packages/admin/pnpm-lock.yaml +5748 -0
- package/templates/blog/pxlr-cms/packages/admin/postcss.config.mjs +9 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/content/[id]/page.tsx +503 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/content/layout.tsx +7 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/content/new/page.tsx +424 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/content/page.tsx +191 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/globals.css +132 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/layout.tsx +25 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/login/page.tsx +119 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/media/layout.tsx +7 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/media/page.tsx +362 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/page.tsx +184 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/profile/layout.tsx +7 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/profile/page.tsx +206 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/schemas/[name]/page.tsx +312 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/schemas/layout.tsx +7 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/schemas/page.tsx +210 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/settings/layout.tsx +7 -0
- package/templates/blog/pxlr-cms/packages/admin/src/app/settings/page.tsx +178 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/editor/media-picker.tsx +202 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/editor/rich-text-editor.tsx +387 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/layout/auth-layout.tsx +43 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/layout/header.tsx +79 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/layout/sidebar.tsx +68 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/providers.tsx +29 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/schema-code-generator.tsx +326 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/avatar.tsx +49 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/button.tsx +55 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/dropdown-menu.tsx +194 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/input.tsx +24 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/label.tsx +25 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/toast.tsx +127 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/toaster.tsx +35 -0
- package/templates/blog/pxlr-cms/packages/admin/src/components/ui/use-toast.ts +187 -0
- package/templates/blog/pxlr-cms/packages/admin/src/lib/api.ts +96 -0
- package/templates/blog/pxlr-cms/packages/admin/src/lib/i18n/context.tsx +60 -0
- package/templates/blog/pxlr-cms/packages/admin/src/lib/i18n/translations.ts +317 -0
- package/templates/blog/pxlr-cms/packages/admin/src/lib/store/auth.ts +51 -0
- package/templates/blog/pxlr-cms/packages/admin/src/lib/utils.ts +29 -0
- package/templates/blog/pxlr-cms/packages/admin/tailwind.config.ts +57 -0
- package/templates/blog/pxlr-cms/packages/admin/tsconfig.json +27 -0
- package/templates/blog/pxlr-cms/packages/api/.env.example +23 -0
- package/templates/blog/pxlr-cms/packages/api/Dockerfile +26 -0
- package/templates/blog/pxlr-cms/packages/api/package.json +42 -0
- package/templates/blog/pxlr-cms/packages/api/src/config.ts +39 -0
- package/templates/blog/pxlr-cms/packages/api/src/database/index.ts +60 -0
- package/templates/blog/pxlr-cms/packages/api/src/database/init.sql +258 -0
- package/templates/blog/pxlr-cms/packages/api/src/database/redis.ts +95 -0
- package/templates/blog/pxlr-cms/packages/api/src/database/seed.sql +78 -0
- package/templates/blog/pxlr-cms/packages/api/src/index.ts +157 -0
- package/templates/blog/pxlr-cms/packages/api/src/modules/auth/routes.ts +256 -0
- package/templates/blog/pxlr-cms/packages/api/src/modules/content/routes.ts +385 -0
- package/templates/blog/pxlr-cms/packages/api/src/modules/media/routes.ts +312 -0
- package/templates/blog/pxlr-cms/packages/api/src/modules/realtime/handler.ts +228 -0
- package/templates/blog/pxlr-cms/packages/api/src/modules/schema/routes.ts +284 -0
- package/templates/blog/pxlr-cms/packages/api/src/modules/versions/routes.ts +70 -0
- package/templates/blog/pxlr-cms/packages/api/tsconfig.json +24 -0
- package/templates/blog/pxlr-cms/packages/shared/package.json +14 -0
- package/templates/blog/pxlr-cms/packages/shared/src/types/index.ts +139 -0
- package/templates/blog/pxlr-cms/packages/shared/tsconfig.json +18 -0
- package/templates/clean/pxlr-cms/README.md +188 -0
- package/templates/clean/pxlr-cms/docker-compose.yml +132 -0
- package/templates/clean/pxlr-cms/nginx/nginx.conf +107 -0
- package/templates/clean/pxlr-cms/packages/admin/.dockerignore +4 -0
- package/templates/clean/pxlr-cms/packages/admin/.env.example +2 -0
- package/templates/clean/pxlr-cms/packages/admin/Dockerfile +19 -0
- package/templates/clean/pxlr-cms/packages/admin/next-env.d.ts +6 -0
- package/templates/clean/pxlr-cms/packages/admin/next.config.ts +22 -0
- package/templates/clean/pxlr-cms/packages/admin/package.json +63 -0
- package/templates/clean/pxlr-cms/packages/admin/pnpm-lock.yaml +5748 -0
- package/templates/clean/pxlr-cms/packages/admin/postcss.config.mjs +9 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/content/[id]/page.tsx +503 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/content/layout.tsx +7 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/content/new/page.tsx +424 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/content/page.tsx +191 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/globals.css +132 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/layout.tsx +25 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/login/page.tsx +119 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/media/layout.tsx +7 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/media/page.tsx +362 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/page.tsx +184 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/profile/layout.tsx +7 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/profile/page.tsx +206 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/schemas/[name]/page.tsx +312 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/schemas/layout.tsx +7 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/schemas/page.tsx +210 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/settings/layout.tsx +7 -0
- package/templates/clean/pxlr-cms/packages/admin/src/app/settings/page.tsx +178 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/editor/media-picker.tsx +202 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/editor/rich-text-editor.tsx +387 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/layout/auth-layout.tsx +43 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/layout/header.tsx +79 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/layout/sidebar.tsx +68 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/providers.tsx +29 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/schema-code-generator.tsx +326 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/avatar.tsx +49 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/button.tsx +55 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/dropdown-menu.tsx +194 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/input.tsx +24 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/label.tsx +25 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/toast.tsx +127 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/toaster.tsx +35 -0
- package/templates/clean/pxlr-cms/packages/admin/src/components/ui/use-toast.ts +187 -0
- package/templates/clean/pxlr-cms/packages/admin/src/lib/api.ts +96 -0
- package/templates/clean/pxlr-cms/packages/admin/src/lib/i18n/context.tsx +60 -0
- package/templates/clean/pxlr-cms/packages/admin/src/lib/i18n/translations.ts +317 -0
- package/templates/clean/pxlr-cms/packages/admin/src/lib/store/auth.ts +51 -0
- package/templates/clean/pxlr-cms/packages/admin/src/lib/utils.ts +29 -0
- package/templates/clean/pxlr-cms/packages/admin/tailwind.config.ts +57 -0
- package/templates/clean/pxlr-cms/packages/admin/tsconfig.json +27 -0
- package/templates/clean/pxlr-cms/packages/api/.env.example +23 -0
- package/templates/clean/pxlr-cms/packages/api/Dockerfile +26 -0
- package/templates/clean/pxlr-cms/packages/api/package.json +42 -0
- package/templates/clean/pxlr-cms/packages/api/src/config.ts +39 -0
- package/templates/clean/pxlr-cms/packages/api/src/database/index.ts +60 -0
- package/templates/clean/pxlr-cms/packages/api/src/database/init.sql +178 -0
- package/templates/clean/pxlr-cms/packages/api/src/database/redis.ts +95 -0
- package/templates/clean/pxlr-cms/packages/api/src/index.ts +157 -0
- package/templates/clean/pxlr-cms/packages/api/src/modules/auth/routes.ts +256 -0
- package/templates/clean/pxlr-cms/packages/api/src/modules/content/routes.ts +385 -0
- package/templates/clean/pxlr-cms/packages/api/src/modules/media/routes.ts +312 -0
- package/templates/clean/pxlr-cms/packages/api/src/modules/realtime/handler.ts +228 -0
- package/templates/clean/pxlr-cms/packages/api/src/modules/schema/routes.ts +284 -0
- package/templates/clean/pxlr-cms/packages/api/src/modules/versions/routes.ts +70 -0
- package/templates/clean/pxlr-cms/packages/api/tsconfig.json +24 -0
- package/templates/clean/pxlr-cms/packages/shared/package.json +14 -0
- package/templates/clean/pxlr-cms/packages/shared/src/types/index.ts +139 -0
- package/templates/clean/pxlr-cms/packages/shared/tsconfig.json +18 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# PXLR CMS
|
|
2
|
+
|
|
3
|
+
Self-hosted Headless CMS inspired by Sanity. Full control over your content management with Docker deployment.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Schema-driven content** - Define content types in code (TypeScript)
|
|
8
|
+
- **Rich text editing** - TipTap-based editor with full formatting support
|
|
9
|
+
- **Media library** - S3-compatible storage with image optimization
|
|
10
|
+
- **Content versioning** - Full version history with diff and rollback
|
|
11
|
+
- **Real-time collaboration** - WebSocket-based presence and updates
|
|
12
|
+
- **Multi-language support** - Built-in i18n for localized content
|
|
13
|
+
- **REST API** - Full CRUD API with Swagger documentation
|
|
14
|
+
- **Modern UI** - React/Next.js admin panel with dark mode
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### Prerequisites
|
|
19
|
+
|
|
20
|
+
- Docker & Docker Compose
|
|
21
|
+
- Node.js 20+ (for local development)
|
|
22
|
+
|
|
23
|
+
### Start with Docker
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd pxlr-cms
|
|
27
|
+
|
|
28
|
+
# Start all services
|
|
29
|
+
docker-compose up -d
|
|
30
|
+
|
|
31
|
+
# Wait for services to initialize (~30 seconds)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Access
|
|
35
|
+
|
|
36
|
+
- **Admin Panel**: http://localhost:3000
|
|
37
|
+
- **API**: http://localhost:4000
|
|
38
|
+
- **API Docs**: http://localhost:4000/docs
|
|
39
|
+
- **MinIO Console**: http://localhost:9001
|
|
40
|
+
|
|
41
|
+
### Default Credentials
|
|
42
|
+
|
|
43
|
+
- **Admin**: admin@pxlr.local / admin123
|
|
44
|
+
- **MinIO**: pxlr_minio / pxlr_minio_secret_2024
|
|
45
|
+
|
|
46
|
+
## Architecture
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
┌─────────────────────────────────────────────────────┐
|
|
50
|
+
│ Docker Network │
|
|
51
|
+
├─────────────────────────────────────────────────────┤
|
|
52
|
+
│ │
|
|
53
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
|
|
54
|
+
│ │ Admin │ │ API │ │ MinIO │ │
|
|
55
|
+
│ │ (Next.js) │──▶│ (Fastify) │──▶│ (S3) │ │
|
|
56
|
+
│ │ :3000 │ │ :4000 │ │ :9000 │ │
|
|
57
|
+
│ └─────────────┘ └──────┬──────┘ └──────────┘ │
|
|
58
|
+
│ │ │
|
|
59
|
+
│ ┌──────▼──────┐ │
|
|
60
|
+
│ │ │ │
|
|
61
|
+
│ ┌─────┴─────┐ ┌─────┴─────┐ │
|
|
62
|
+
│ │ PostgreSQL│ │ Redis │ │
|
|
63
|
+
│ │ :5432 │ │ :6379 │ │
|
|
64
|
+
│ └───────────┘ └───────────┘ │
|
|
65
|
+
│ │
|
|
66
|
+
└─────────────────────────────────────────────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Project Structure
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
pxlr-cms/
|
|
73
|
+
├── docker-compose.yml # Docker services configuration
|
|
74
|
+
├── nginx/ # Nginx reverse proxy config
|
|
75
|
+
├── packages/
|
|
76
|
+
│ ├── api/ # Backend API (Fastify + TypeScript)
|
|
77
|
+
│ │ ├── src/
|
|
78
|
+
│ │ │ ├── modules/ # API modules (auth, content, schema, media)
|
|
79
|
+
│ │ │ ├── database/ # Database connection and queries
|
|
80
|
+
│ │ │ └── index.ts # Entry point
|
|
81
|
+
│ │ └── Dockerfile
|
|
82
|
+
│ │
|
|
83
|
+
│ ├── admin/ # Admin Panel (Next.js)
|
|
84
|
+
│ │ ├── src/
|
|
85
|
+
│ │ │ ├── app/ # Next.js app router pages
|
|
86
|
+
│ │ │ ├── components/# React components
|
|
87
|
+
│ │ │ └── lib/ # Utilities and stores
|
|
88
|
+
│ │ └── Dockerfile
|
|
89
|
+
│ │
|
|
90
|
+
│ └── shared/ # Shared TypeScript types
|
|
91
|
+
│ └── src/types/
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## API Endpoints
|
|
95
|
+
|
|
96
|
+
### Authentication
|
|
97
|
+
- `POST /auth/login` - Login
|
|
98
|
+
- `POST /auth/register` - Register new user
|
|
99
|
+
- `GET /auth/me` - Get current user
|
|
100
|
+
- `POST /auth/logout` - Logout
|
|
101
|
+
|
|
102
|
+
### Schemas
|
|
103
|
+
- `GET /schemas` - List all schemas
|
|
104
|
+
- `GET /schemas/:name` - Get schema by name
|
|
105
|
+
- `POST /schemas` - Create schema
|
|
106
|
+
- `PUT /schemas/:name` - Update schema
|
|
107
|
+
- `DELETE /schemas/:name` - Delete schema
|
|
108
|
+
|
|
109
|
+
### Content
|
|
110
|
+
- `GET /content` - List documents (with filtering)
|
|
111
|
+
- `GET /content/:id` - Get document
|
|
112
|
+
- `POST /content` - Create document
|
|
113
|
+
- `PUT /content/:id` - Update document
|
|
114
|
+
- `DELETE /content/:id` - Delete document
|
|
115
|
+
- `GET /content/:id/versions` - Get version history
|
|
116
|
+
- `POST /content/:id/versions/:version/restore` - Restore version
|
|
117
|
+
|
|
118
|
+
### Media
|
|
119
|
+
- `GET /media` - List media files
|
|
120
|
+
- `GET /media/:id` - Get file
|
|
121
|
+
- `POST /media/upload` - Upload file
|
|
122
|
+
- `PUT /media/:id` - Update metadata
|
|
123
|
+
- `DELETE /media/:id` - Delete file
|
|
124
|
+
|
|
125
|
+
## Local Development
|
|
126
|
+
|
|
127
|
+
### API Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
cd packages/api
|
|
131
|
+
npm install
|
|
132
|
+
npm run dev
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Admin Development
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cd packages/admin
|
|
139
|
+
npm install
|
|
140
|
+
npm run dev
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Environment Variables
|
|
144
|
+
|
|
145
|
+
### API (.env)
|
|
146
|
+
```env
|
|
147
|
+
NODE_ENV=development
|
|
148
|
+
PORT=4000
|
|
149
|
+
DATABASE_URL=postgresql://pxlr:pxlr_secret_2024@localhost:5432/pxlr_cms
|
|
150
|
+
REDIS_URL=redis://localhost:6379
|
|
151
|
+
MINIO_ENDPOINT=localhost
|
|
152
|
+
MINIO_PORT=9000
|
|
153
|
+
MINIO_ACCESS_KEY=pxlr_minio
|
|
154
|
+
MINIO_SECRET_KEY=pxlr_minio_secret_2024
|
|
155
|
+
MINIO_BUCKET=pxlr-media
|
|
156
|
+
JWT_SECRET=your_jwt_secret_here
|
|
157
|
+
JWT_EXPIRES_IN=7d
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Admin (.env.local)
|
|
161
|
+
```env
|
|
162
|
+
NEXT_PUBLIC_API_URL=http://localhost:4000
|
|
163
|
+
NEXT_PUBLIC_WS_URL=ws://localhost:4000
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Production Deployment
|
|
167
|
+
|
|
168
|
+
1. Update environment variables with secure values
|
|
169
|
+
2. Enable Nginx profile in docker-compose:
|
|
170
|
+
```bash
|
|
171
|
+
docker-compose --profile production up -d
|
|
172
|
+
```
|
|
173
|
+
3. Configure SSL/TLS certificates
|
|
174
|
+
4. Set up backups for PostgreSQL and MinIO
|
|
175
|
+
|
|
176
|
+
## Technology Stack
|
|
177
|
+
|
|
178
|
+
- **Backend**: Node.js, Fastify, TypeScript
|
|
179
|
+
- **Frontend**: Next.js 15, React 19, TailwindCSS
|
|
180
|
+
- **Database**: PostgreSQL 16
|
|
181
|
+
- **Cache**: Redis 7
|
|
182
|
+
- **Storage**: MinIO (S3-compatible)
|
|
183
|
+
- **Editor**: TipTap
|
|
184
|
+
- **State**: Zustand, React Query
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT License
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
services:
|
|
2
|
+
# PostgreSQL Database
|
|
3
|
+
postgres:
|
|
4
|
+
image: postgres:16-alpine
|
|
5
|
+
container_name: pxlr-postgres
|
|
6
|
+
restart: unless-stopped
|
|
7
|
+
environment:
|
|
8
|
+
POSTGRES_USER: pxlr
|
|
9
|
+
POSTGRES_PASSWORD: pxlr_secret_2024
|
|
10
|
+
POSTGRES_DB: pxlr_cms
|
|
11
|
+
volumes:
|
|
12
|
+
- postgres_data:/var/lib/postgresql/data
|
|
13
|
+
- ./packages/api/src/database/init.sql:/docker-entrypoint-initdb.d/init.sql
|
|
14
|
+
ports:
|
|
15
|
+
- "5432:5432"
|
|
16
|
+
healthcheck:
|
|
17
|
+
test: ["CMD-SHELL", "pg_isready -U pxlr -d pxlr_cms"]
|
|
18
|
+
interval: 10s
|
|
19
|
+
timeout: 5s
|
|
20
|
+
retries: 5
|
|
21
|
+
|
|
22
|
+
# Redis for caching and pub/sub
|
|
23
|
+
redis:
|
|
24
|
+
image: redis:7-alpine
|
|
25
|
+
container_name: pxlr-redis
|
|
26
|
+
restart: unless-stopped
|
|
27
|
+
command: redis-server --appendonly yes
|
|
28
|
+
volumes:
|
|
29
|
+
- redis_data:/data
|
|
30
|
+
ports:
|
|
31
|
+
- "6379:6379"
|
|
32
|
+
healthcheck:
|
|
33
|
+
test: ["CMD", "redis-cli", "ping"]
|
|
34
|
+
interval: 10s
|
|
35
|
+
timeout: 5s
|
|
36
|
+
retries: 5
|
|
37
|
+
|
|
38
|
+
# MinIO S3-compatible storage for media
|
|
39
|
+
minio:
|
|
40
|
+
image: minio/minio:latest
|
|
41
|
+
container_name: pxlr-minio
|
|
42
|
+
restart: unless-stopped
|
|
43
|
+
command: server /data --console-address ":9011"
|
|
44
|
+
environment:
|
|
45
|
+
MINIO_ROOT_USER: pxlr_minio
|
|
46
|
+
MINIO_ROOT_PASSWORD: pxlr_minio_secret_2024
|
|
47
|
+
volumes:
|
|
48
|
+
- minio_data:/data
|
|
49
|
+
ports:
|
|
50
|
+
- "9010:9000"
|
|
51
|
+
- "9011:9011"
|
|
52
|
+
healthcheck:
|
|
53
|
+
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
|
54
|
+
interval: 30s
|
|
55
|
+
timeout: 20s
|
|
56
|
+
retries: 3
|
|
57
|
+
|
|
58
|
+
# Backend API
|
|
59
|
+
api:
|
|
60
|
+
build:
|
|
61
|
+
context: ./packages/api
|
|
62
|
+
dockerfile: Dockerfile
|
|
63
|
+
container_name: pxlr-api
|
|
64
|
+
restart: unless-stopped
|
|
65
|
+
environment:
|
|
66
|
+
NODE_ENV: development
|
|
67
|
+
PORT: 4000
|
|
68
|
+
DATABASE_URL: postgresql://pxlr:pxlr_secret_2024@postgres:5432/pxlr_cms
|
|
69
|
+
REDIS_URL: redis://redis:6379
|
|
70
|
+
MINIO_ENDPOINT: minio
|
|
71
|
+
MINIO_PORT: "9000"
|
|
72
|
+
MINIO_ACCESS_KEY: pxlr_minio
|
|
73
|
+
MINIO_SECRET_KEY: pxlr_minio_secret_2024
|
|
74
|
+
MINIO_BUCKET: pxlr-media
|
|
75
|
+
JWT_SECRET: pxlr_jwt_secret_change_in_production_2024
|
|
76
|
+
JWT_EXPIRES_IN: 7d
|
|
77
|
+
ports:
|
|
78
|
+
- "4000:4000"
|
|
79
|
+
depends_on:
|
|
80
|
+
postgres:
|
|
81
|
+
condition: service_healthy
|
|
82
|
+
redis:
|
|
83
|
+
condition: service_healthy
|
|
84
|
+
minio:
|
|
85
|
+
condition: service_healthy
|
|
86
|
+
volumes:
|
|
87
|
+
- ./packages/api:/app
|
|
88
|
+
- /app/node_modules
|
|
89
|
+
|
|
90
|
+
# Admin Panel
|
|
91
|
+
admin:
|
|
92
|
+
build:
|
|
93
|
+
context: ./packages/admin
|
|
94
|
+
dockerfile: Dockerfile
|
|
95
|
+
container_name: pxlr-admin
|
|
96
|
+
restart: unless-stopped
|
|
97
|
+
environment:
|
|
98
|
+
NODE_ENV: development
|
|
99
|
+
NEXT_PUBLIC_API_URL: http://localhost:4000
|
|
100
|
+
NEXT_PUBLIC_WS_URL: ws://localhost:4000
|
|
101
|
+
ports:
|
|
102
|
+
- "3333:3000"
|
|
103
|
+
depends_on:
|
|
104
|
+
- api
|
|
105
|
+
volumes:
|
|
106
|
+
- ./packages/admin:/app
|
|
107
|
+
- /app/node_modules
|
|
108
|
+
- /app/.next
|
|
109
|
+
|
|
110
|
+
# Nginx reverse proxy (for production)
|
|
111
|
+
nginx:
|
|
112
|
+
image: nginx:alpine
|
|
113
|
+
container_name: pxlr-nginx
|
|
114
|
+
restart: unless-stopped
|
|
115
|
+
ports:
|
|
116
|
+
- "80:80"
|
|
117
|
+
volumes:
|
|
118
|
+
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
|
119
|
+
depends_on:
|
|
120
|
+
- api
|
|
121
|
+
- admin
|
|
122
|
+
profiles:
|
|
123
|
+
- production
|
|
124
|
+
|
|
125
|
+
volumes:
|
|
126
|
+
postgres_data:
|
|
127
|
+
redis_data:
|
|
128
|
+
minio_data:
|
|
129
|
+
|
|
130
|
+
networks:
|
|
131
|
+
default:
|
|
132
|
+
name: pxlr-network
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
events {
|
|
2
|
+
worker_connections 1024;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
http {
|
|
6
|
+
include /etc/nginx/mime.types;
|
|
7
|
+
default_type application/octet-stream;
|
|
8
|
+
|
|
9
|
+
# Logging
|
|
10
|
+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
11
|
+
'$status $body_bytes_sent "$http_referer" '
|
|
12
|
+
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
13
|
+
|
|
14
|
+
access_log /var/log/nginx/access.log main;
|
|
15
|
+
error_log /var/log/nginx/error.log warn;
|
|
16
|
+
|
|
17
|
+
# Performance
|
|
18
|
+
sendfile on;
|
|
19
|
+
tcp_nopush on;
|
|
20
|
+
tcp_nodelay on;
|
|
21
|
+
keepalive_timeout 65;
|
|
22
|
+
types_hash_max_size 2048;
|
|
23
|
+
|
|
24
|
+
# Gzip compression
|
|
25
|
+
gzip on;
|
|
26
|
+
gzip_vary on;
|
|
27
|
+
gzip_proxied any;
|
|
28
|
+
gzip_comp_level 6;
|
|
29
|
+
gzip_types text/plain text/css text/xml application/json application/javascript
|
|
30
|
+
application/xml application/xml+rss text/javascript application/x-javascript;
|
|
31
|
+
|
|
32
|
+
# Rate limiting
|
|
33
|
+
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
|
34
|
+
|
|
35
|
+
# Upstream servers
|
|
36
|
+
upstream api_backend {
|
|
37
|
+
server api:4000;
|
|
38
|
+
keepalive 32;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
upstream admin_frontend {
|
|
42
|
+
server admin:3000;
|
|
43
|
+
keepalive 32;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
server {
|
|
47
|
+
listen 80;
|
|
48
|
+
server_name localhost;
|
|
49
|
+
|
|
50
|
+
# Security headers
|
|
51
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
52
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
53
|
+
add_header X-XSS-Protection "1; mode=block" always;
|
|
54
|
+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
55
|
+
|
|
56
|
+
# Max upload size
|
|
57
|
+
client_max_body_size 100M;
|
|
58
|
+
|
|
59
|
+
# API routes
|
|
60
|
+
location /api/ {
|
|
61
|
+
limit_req zone=api_limit burst=20 nodelay;
|
|
62
|
+
|
|
63
|
+
proxy_pass http://api_backend/;
|
|
64
|
+
proxy_http_version 1.1;
|
|
65
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
66
|
+
proxy_set_header Connection "upgrade";
|
|
67
|
+
proxy_set_header Host $host;
|
|
68
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
69
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
70
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
71
|
+
proxy_cache_bypass $http_upgrade;
|
|
72
|
+
proxy_read_timeout 300s;
|
|
73
|
+
proxy_connect_timeout 75s;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# WebSocket for real-time
|
|
77
|
+
location /ws/ {
|
|
78
|
+
proxy_pass http://api_backend/ws/;
|
|
79
|
+
proxy_http_version 1.1;
|
|
80
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
81
|
+
proxy_set_header Connection "upgrade";
|
|
82
|
+
proxy_set_header Host $host;
|
|
83
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
84
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
85
|
+
proxy_read_timeout 86400s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Admin panel (default)
|
|
89
|
+
location / {
|
|
90
|
+
proxy_pass http://admin_frontend;
|
|
91
|
+
proxy_http_version 1.1;
|
|
92
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
93
|
+
proxy_set_header Connection "upgrade";
|
|
94
|
+
proxy_set_header Host $host;
|
|
95
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
96
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
97
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Health check endpoint
|
|
101
|
+
location /health {
|
|
102
|
+
access_log off;
|
|
103
|
+
return 200 "OK";
|
|
104
|
+
add_header Content-Type text/plain;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# PXLR CMS Admin Panel Dockerfile
|
|
2
|
+
FROM node:20-alpine
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Copy package files
|
|
7
|
+
COPY package.json ./
|
|
8
|
+
|
|
9
|
+
# Install dependencies
|
|
10
|
+
RUN npm cache clean --force && npm install --legacy-peer-deps
|
|
11
|
+
|
|
12
|
+
# Copy source code
|
|
13
|
+
COPY . .
|
|
14
|
+
|
|
15
|
+
# Expose port
|
|
16
|
+
EXPOSE 3000
|
|
17
|
+
|
|
18
|
+
# Start development server
|
|
19
|
+
CMD ["npm", "run", "dev"]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="next" />
|
|
2
|
+
/// <reference types="next/image-types/global" />
|
|
3
|
+
/// <reference path="./.next/types/routes.d.ts" />
|
|
4
|
+
|
|
5
|
+
// NOTE: This file should not be edited
|
|
6
|
+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NextConfig } from 'next';
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
devIndicators: false,
|
|
5
|
+
output: 'standalone',
|
|
6
|
+
images: {
|
|
7
|
+
remotePatterns: [
|
|
8
|
+
{
|
|
9
|
+
protocol: 'http',
|
|
10
|
+
hostname: 'localhost',
|
|
11
|
+
port: '9000',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
protocol: 'http',
|
|
15
|
+
hostname: 'minio',
|
|
16
|
+
port: '9000',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default nextConfig;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pxlr/admin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "PXLR CMS Admin Panel",
|
|
5
|
+
"private": true,
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "next dev",
|
|
8
|
+
"build": "next build",
|
|
9
|
+
"start": "next start",
|
|
10
|
+
"lint": "next lint"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@hookform/resolvers": "^3.9.0",
|
|
14
|
+
"@radix-ui/react-alert-dialog": "^1.1.2",
|
|
15
|
+
"@radix-ui/react-avatar": "^1.1.1",
|
|
16
|
+
"@radix-ui/react-dialog": "^1.1.2",
|
|
17
|
+
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
|
18
|
+
"@radix-ui/react-label": "^2.1.0",
|
|
19
|
+
"@radix-ui/react-select": "^2.1.2",
|
|
20
|
+
"@radix-ui/react-separator": "^1.1.0",
|
|
21
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
22
|
+
"@radix-ui/react-tabs": "^1.1.1",
|
|
23
|
+
"@radix-ui/react-toast": "^1.2.2",
|
|
24
|
+
"@radix-ui/react-tooltip": "^1.1.3",
|
|
25
|
+
"@tanstack/react-query": "^5.59.0",
|
|
26
|
+
"@tiptap/extension-color": "^2.10.0",
|
|
27
|
+
"@tiptap/extension-highlight": "^2.10.0",
|
|
28
|
+
"@tiptap/extension-image": "^2.10.0",
|
|
29
|
+
"@tiptap/extension-link": "^2.10.0",
|
|
30
|
+
"@tiptap/extension-placeholder": "^2.10.0",
|
|
31
|
+
"@tiptap/extension-text-align": "^2.10.0",
|
|
32
|
+
"@tiptap/extension-text-style": "^2.10.0",
|
|
33
|
+
"@tiptap/extension-underline": "^2.10.0",
|
|
34
|
+
"@tiptap/pm": "^2.10.0",
|
|
35
|
+
"@tiptap/react": "^2.10.0",
|
|
36
|
+
"@tiptap/starter-kit": "^2.10.0",
|
|
37
|
+
"class-variance-authority": "^0.7.0",
|
|
38
|
+
"clsx": "^2.1.1",
|
|
39
|
+
"date-fns": "^4.1.0",
|
|
40
|
+
"lucide-react": "^0.453.0",
|
|
41
|
+
"next": "^15.1.0",
|
|
42
|
+
"react": "^18.3.1",
|
|
43
|
+
"react-dom": "^18.3.1",
|
|
44
|
+
"react-dropzone": "^14.2.9",
|
|
45
|
+
"react-hook-form": "^7.53.0",
|
|
46
|
+
"socket.io-client": "^4.8.0",
|
|
47
|
+
"tailwind-merge": "^2.5.3",
|
|
48
|
+
"tailwindcss-animate": "^1.0.7",
|
|
49
|
+
"zod": "^3.23.8",
|
|
50
|
+
"zustand": "^5.0.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.0.0",
|
|
54
|
+
"@types/react": "^18.3.0",
|
|
55
|
+
"@types/react-dom": "^18.3.0",
|
|
56
|
+
"autoprefixer": "^10.4.20",
|
|
57
|
+
"eslint": "^9.0.0",
|
|
58
|
+
"eslint-config-next": "^15.1.0",
|
|
59
|
+
"postcss": "^8.4.47",
|
|
60
|
+
"tailwindcss": "^3.4.14",
|
|
61
|
+
"typescript": "^5.6.0"
|
|
62
|
+
}
|
|
63
|
+
}
|