workers-sentinel 0.1.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 +341 -0
- package/dist/README.md +1 -0
- package/dist/dashboard/assets/EventDetail-DgqYLyiF.js +1 -0
- package/dist/dashboard/assets/IssueDetail-CjG-J6cD.js +1 -0
- package/dist/dashboard/assets/Issues-Cfnqb_a0.js +33 -0
- package/dist/dashboard/assets/Layout-Edli6x7p.js +1 -0
- package/dist/dashboard/assets/Login-DHYJjZXJ.js +1 -0
- package/dist/dashboard/assets/ProjectCreate-D5GeKxHe.js +33 -0
- package/dist/dashboard/assets/ProjectSettings-6OwGPcir.js +1 -0
- package/dist/dashboard/assets/Projects-Bth0OyBs.js +1 -0
- package/dist/dashboard/assets/Register-C3zVYtDV.js +1 -0
- package/dist/dashboard/assets/index-7dRN2NZv.js +30 -0
- package/dist/dashboard/assets/index-JMAa_1Q2.css +1 -0
- package/dist/dashboard/assets/projects-BUj8O17i.js +1 -0
- package/dist/dashboard/index.html +14 -0
- package/dist/durable-objects/auth-state.d.ts +28 -0
- package/dist/durable-objects/auth-state.d.ts.map +1 -0
- package/dist/durable-objects/project-state.d.ts +23 -0
- package/dist/durable-objects/project-state.d.ts.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4774 -0
- package/dist/index.js.map +8 -0
- package/dist/lib/envelope-parser.d.ts +38 -0
- package/dist/lib/envelope-parser.d.ts.map +1 -0
- package/dist/lib/fingerprint.d.ts +23 -0
- package/dist/lib/fingerprint.d.ts.map +1 -0
- package/dist/middleware/auth.d.ts +10 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/routes/auth.d.ts +6 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/events.d.ts +11 -0
- package/dist/routes/events.d.ts.map +1 -0
- package/dist/routes/ingestion.d.ts +6 -0
- package/dist/routes/ingestion.d.ts.map +1 -0
- package/dist/routes/issues.d.ts +11 -0
- package/dist/routes/issues.d.ts.map +1 -0
- package/dist/routes/projects.d.ts +11 -0
- package/dist/routes/projects.d.ts.map +1 -0
- package/dist/rpc.d.ts +59 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/types.d.ts +166 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gabriel Massadas
|
|
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,341 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="#">
|
|
3
|
+
<h1 style="font-size: 4rem;">🛡️</h1>
|
|
4
|
+
<h1>Workers Sentinel</h1>
|
|
5
|
+
</a>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<em>A self-hosted, Sentry-compatible error tracking system running entirely on Cloudflare Workers</em>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://github.com/G4brym/workers-sentinel/commits/main" target="_blank">
|
|
14
|
+
<img src="https://img.shields.io/github/commit-activity/m/G4brym/workers-sentinel?label=Commits&style=social" alt="Workers Sentinel Commits">
|
|
15
|
+
</a>
|
|
16
|
+
<a href="https://github.com/G4brym/workers-sentinel/issues" target="_blank">
|
|
17
|
+
<img src="https://img.shields.io/github/issues/G4brym/workers-sentinel?style=social" alt="Issues">
|
|
18
|
+
</a>
|
|
19
|
+
<a href="https://github.com/G4brym/workers-sentinel/blob/main/LICENSE" target="_blank">
|
|
20
|
+
<img src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=social" alt="Software License">
|
|
21
|
+
</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
# Workers Sentinel
|
|
25
|
+
|
|
26
|
+
Workers Sentinel is a lightweight, self-hosted error tracking and monitoring solution that runs entirely on your Cloudflare account. It accepts events using the Sentry SDK wire format, allowing you to use existing Sentry SDKs by simply changing the DSN endpoint. All your error data is stored securely in SQLite-backed Durable Objects, giving you full control over your information.
|
|
27
|
+
|
|
28
|
+
[](https://deploy.workers.cloudflare.com/?url=https://github.com/G4brym/workers-sentinel/tree/main/template)
|
|
29
|
+
|
|
30
|
+
## Table of Contents
|
|
31
|
+
|
|
32
|
+
- [Overview](#overview)
|
|
33
|
+
- [Why Workers Sentinel?](#why-workers-sentinel)
|
|
34
|
+
- [Key Features](#key-features)
|
|
35
|
+
- [Prerequisites](#prerequisites)
|
|
36
|
+
- [Getting Started](#getting-started)
|
|
37
|
+
- [SDK Configuration](#sdk-configuration)
|
|
38
|
+
- [Architecture](#architecture)
|
|
39
|
+
- [Roadmap & Future Enhancements](#roadmap--future-enhancements)
|
|
40
|
+
- [Known Limitations](#known-limitations)
|
|
41
|
+
- [Contributing](#contributing)
|
|
42
|
+
- [License](#license)
|
|
43
|
+
|
|
44
|
+
## Overview
|
|
45
|
+
|
|
46
|
+
Workers Sentinel gives you a private, self-hosted error tracking solution with a modern web dashboard. By leveraging the Cloudflare ecosystem, it offers a cost-effective and scalable alternative to hosted error tracking services. All your data is stored in your own Durable Objects with SQLite storage, giving you complete control and privacy.
|
|
47
|
+
|
|
48
|
+
## Why Workers Sentinel?
|
|
49
|
+
|
|
50
|
+
**🔒 Privacy First**
|
|
51
|
+
- All data stays in YOUR Cloudflare account
|
|
52
|
+
- No third-party tracking or data sharing
|
|
53
|
+
- You control your error data completely
|
|
54
|
+
|
|
55
|
+
**💰 Cost-Effective**
|
|
56
|
+
- Runs on Cloudflare's generous free tier
|
|
57
|
+
- Pay only for what you use beyond free limits
|
|
58
|
+
- No monthly subscription fees
|
|
59
|
+
|
|
60
|
+
**⚡ Performance**
|
|
61
|
+
- Built on Cloudflare's global edge network
|
|
62
|
+
- Fast error ingestion worldwide
|
|
63
|
+
- Serverless architecture scales automatically
|
|
64
|
+
|
|
65
|
+
**🔌 SDK Compatible**
|
|
66
|
+
- Works with existing Sentry SDKs
|
|
67
|
+
- Just change your DSN endpoint
|
|
68
|
+
- Supports JavaScript, Python, Go, and more
|
|
69
|
+
|
|
70
|
+
**🎨 Modern Dashboard**
|
|
71
|
+
- Clean, intuitive interface
|
|
72
|
+
- Stack trace visualization
|
|
73
|
+
- Issue grouping and management
|
|
74
|
+
|
|
75
|
+
**🛠️ Easy Setup**
|
|
76
|
+
- Deploy with one click
|
|
77
|
+
- Automatic project creation
|
|
78
|
+
- Smart authentication setup
|
|
79
|
+
|
|
80
|
+
## Key Features
|
|
81
|
+
|
|
82
|
+
- **🔌 Sentry SDK Compatible**: Use existing Sentry SDKs by changing only the DSN endpoint
|
|
83
|
+
- **🔒 Secure & Private**: Self-hosted on your Cloudflare account with no third-party data access
|
|
84
|
+
- **🔐 Smart Authentication**: Automatic first-user admin setup with session-based auth
|
|
85
|
+
- **📊 Issue Grouping**: Automatic fingerprinting groups similar errors into issues
|
|
86
|
+
- **📈 Event Statistics**: Track error frequency with hourly aggregations
|
|
87
|
+
- **🔍 Stack Traces**: Full stack trace visualization with code context
|
|
88
|
+
- **👥 User Tracking**: See how many users are affected by each issue
|
|
89
|
+
- **🏷️ Tags & Context**: View tags, breadcrumbs, and contextual data
|
|
90
|
+
- **✅ Issue Management**: Mark issues as resolved or ignored
|
|
91
|
+
- **🌐 Multi-Project**: Create multiple projects with isolated data storage
|
|
92
|
+
|
|
93
|
+
## Prerequisites
|
|
94
|
+
|
|
95
|
+
Before deploying Workers Sentinel, make sure you have:
|
|
96
|
+
|
|
97
|
+
- **Cloudflare Account** - [Sign up for free](https://dash.cloudflare.com/sign-up)
|
|
98
|
+
- **Node.js 20+** - For local development (not required for one-click deployment)
|
|
99
|
+
|
|
100
|
+
**Cloudflare Services Used:**
|
|
101
|
+
- Workers (Compute)
|
|
102
|
+
- Durable Objects with SQLite (State management)
|
|
103
|
+
- Workers Assets (Dashboard hosting)
|
|
104
|
+
|
|
105
|
+
All these services have generous free tiers sufficient for most use cases.
|
|
106
|
+
|
|
107
|
+
## Getting Started
|
|
108
|
+
|
|
109
|
+
### One-Click Deploy
|
|
110
|
+
|
|
111
|
+
Use the "Deploy to Cloudflare" button above, or run:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm create cloudflare@latest -- --template=https://github.com/G4brym/workers-sentinel/tree/main/template
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Manual Deployment
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Clone the repository
|
|
121
|
+
git clone https://github.com/G4brym/workers-sentinel.git
|
|
122
|
+
cd workers-sentinel
|
|
123
|
+
|
|
124
|
+
# Install dependencies
|
|
125
|
+
pnpm install
|
|
126
|
+
|
|
127
|
+
# Build and deploy
|
|
128
|
+
pnpm deploy
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### First-Time Setup
|
|
132
|
+
|
|
133
|
+
1. **Deploy your worker** to Cloudflare
|
|
134
|
+
2. **Visit your worker URL** in a browser
|
|
135
|
+
3. **Register the first user** - this becomes your admin account
|
|
136
|
+
4. **Create your first project** - you'll receive a DSN
|
|
137
|
+
5. **Configure your Sentry SDK** with the DSN
|
|
138
|
+
|
|
139
|
+
## SDK Configuration
|
|
140
|
+
|
|
141
|
+
Workers Sentinel is compatible with official Sentry SDKs. Simply use your Sentinel DSN instead of a Sentry DSN.
|
|
142
|
+
|
|
143
|
+
### Cloudflare Workers (Service Binding with RPC)
|
|
144
|
+
|
|
145
|
+
For Cloudflare Workers, you can use [service bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings/) with RPC for optimal performance. This routes requests internally within Cloudflare's network, avoiding external HTTP roundtrips and reducing latency.
|
|
146
|
+
|
|
147
|
+
**Step 1:** Add a service binding to your worker's `wrangler.jsonc`:
|
|
148
|
+
|
|
149
|
+
```jsonc
|
|
150
|
+
{
|
|
151
|
+
"name": "my-worker",
|
|
152
|
+
"main": "src/index.ts",
|
|
153
|
+
"compatibility_flags": ["nodejs_als"],
|
|
154
|
+
"services": [
|
|
155
|
+
{ "binding": "SENTINEL", "service": "workers-sentinel", "entrypoint": "SentinelRpc" }
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Step 2:** Initialize Sentry with the RPC transport:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import * as Sentry from '@sentry/cloudflare';
|
|
164
|
+
import { waitUntil } from 'cloudflare:workers';
|
|
165
|
+
|
|
166
|
+
const DSN = 'https://<public_key>@<your-worker>.workers.dev/<project_id>';
|
|
167
|
+
|
|
168
|
+
export default Sentry.withSentry(
|
|
169
|
+
(env: Env) => ({
|
|
170
|
+
dsn: DSN,
|
|
171
|
+
transport: () => ({
|
|
172
|
+
send: async (envelope) => {
|
|
173
|
+
const rpcPromise = env.SENTINEL.captureEnvelope(DSN, envelope);
|
|
174
|
+
waitUntil(rpcPromise);
|
|
175
|
+
const result = await rpcPromise;
|
|
176
|
+
return { statusCode: result.status };
|
|
177
|
+
},
|
|
178
|
+
flush: async () => true,
|
|
179
|
+
}),
|
|
180
|
+
}),
|
|
181
|
+
{
|
|
182
|
+
async fetch(request, env, ctx) {
|
|
183
|
+
// Your worker code here
|
|
184
|
+
return new Response('Hello!');
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The `waitUntil` call ensures the RPC completes even after the HTTP response returns. The `captureEnvelope` method takes the DSN and envelope, handling authentication and ingestion internally.
|
|
191
|
+
|
|
192
|
+
### JavaScript / Browser
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
import * as Sentry from '@sentry/browser';
|
|
196
|
+
|
|
197
|
+
Sentry.init({
|
|
198
|
+
dsn: 'https://<public_key>@<your-worker>.workers.dev/<project_id>',
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Node.js
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
const Sentry = require('@sentry/node');
|
|
206
|
+
|
|
207
|
+
Sentry.init({
|
|
208
|
+
dsn: 'https://<public_key>@<your-worker>.workers.dev/<project_id>',
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Python
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
import sentry_sdk
|
|
216
|
+
|
|
217
|
+
sentry_sdk.init(
|
|
218
|
+
dsn="https://<public_key>@<your-worker>.workers.dev/<project_id>",
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Go
|
|
223
|
+
|
|
224
|
+
```go
|
|
225
|
+
import "github.com/getsentry/sentry-go"
|
|
226
|
+
|
|
227
|
+
sentry.Init(sentry.ClientOptions{
|
|
228
|
+
Dsn: "https://<public_key>@<your-worker>.workers.dev/<project_id>",
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
The DSN is displayed when you create a project in the dashboard, or you can find it in the project settings.
|
|
233
|
+
|
|
234
|
+
## Architecture
|
|
235
|
+
|
|
236
|
+
Workers Sentinel is built with modern web technologies:
|
|
237
|
+
|
|
238
|
+
**Backend (Worker):**
|
|
239
|
+
- **Hono** - Fast, lightweight web framework
|
|
240
|
+
- **Cloudflare Durable Objects** - Distributed state with SQLite storage
|
|
241
|
+
- **Sentry Envelope Parser** - Compatible with Sentry SDK wire format
|
|
242
|
+
|
|
243
|
+
**Frontend (Dashboard):**
|
|
244
|
+
- **Vue.js 3** - Progressive JavaScript framework
|
|
245
|
+
- **TypeScript** - Type-safe development
|
|
246
|
+
- **Tailwind CSS** - Utility-first styling
|
|
247
|
+
- **Pinia** - State management
|
|
248
|
+
- **Vite** - Fast build tooling
|
|
249
|
+
|
|
250
|
+
**Data Architecture:**
|
|
251
|
+
- **AuthState DO** (singleton) - Users, sessions, project registry
|
|
252
|
+
- **ProjectState DO** (per-project) - Issues, events, statistics
|
|
253
|
+
|
|
254
|
+
Each project has its own isolated Durable Object with SQLite storage, ensuring data isolation and scalability.
|
|
255
|
+
|
|
256
|
+
## Roadmap & Future Enhancements
|
|
257
|
+
|
|
258
|
+
Planned features for future releases:
|
|
259
|
+
|
|
260
|
+
- [ ] Source map support for JavaScript errors
|
|
261
|
+
- [ ] Release tracking and deployment correlation
|
|
262
|
+
- [ ] Performance monitoring (transactions, spans)
|
|
263
|
+
- [ ] Alerting integrations (webhook, email)
|
|
264
|
+
- [ ] Session replay support
|
|
265
|
+
- [ ] Team/organization support
|
|
266
|
+
- [ ] Issue assignment
|
|
267
|
+
- [ ] Search functionality
|
|
268
|
+
- [ ] Time-series charts
|
|
269
|
+
- [ ] Rate limiting per project
|
|
270
|
+
- [ ] Event retention policies
|
|
271
|
+
|
|
272
|
+
## Known Limitations
|
|
273
|
+
|
|
274
|
+
**Current Limitations:**
|
|
275
|
+
- No source map support yet (stack traces show minified code)
|
|
276
|
+
- No performance monitoring (error tracking only)
|
|
277
|
+
- No alerting/notifications
|
|
278
|
+
- Single-user admin (no team management yet)
|
|
279
|
+
|
|
280
|
+
**Sentry Feature Parity:**
|
|
281
|
+
Workers Sentinel focuses on core error tracking. Advanced Sentry features like:
|
|
282
|
+
- Session replay
|
|
283
|
+
- Performance monitoring
|
|
284
|
+
- Profiling
|
|
285
|
+
- Crons monitoring
|
|
286
|
+
|
|
287
|
+
Are not currently supported but may be added in future versions.
|
|
288
|
+
|
|
289
|
+
**Browser Compatibility:**
|
|
290
|
+
- Modern browsers required (Chrome 90+, Firefox 88+, Safari 14+)
|
|
291
|
+
- JavaScript must be enabled
|
|
292
|
+
- Cookies must be enabled for authentication
|
|
293
|
+
|
|
294
|
+
## Contributing
|
|
295
|
+
|
|
296
|
+
We welcome contributions from the community!
|
|
297
|
+
|
|
298
|
+
**🐛 Bug Reports**
|
|
299
|
+
- Use the [GitHub Issues](https://github.com/G4brym/workers-sentinel/issues) page
|
|
300
|
+
- Include reproduction steps
|
|
301
|
+
- Specify your environment
|
|
302
|
+
|
|
303
|
+
**✨ Feature Requests**
|
|
304
|
+
- Check existing issues first
|
|
305
|
+
- Explain the use case and benefit
|
|
306
|
+
|
|
307
|
+
**💻 Code Contributions**
|
|
308
|
+
1. Fork the repository
|
|
309
|
+
2. Create a feature branch
|
|
310
|
+
3. Make your changes
|
|
311
|
+
4. Submit a Pull Request
|
|
312
|
+
|
|
313
|
+
**Development Setup:**
|
|
314
|
+
```bash
|
|
315
|
+
# Clone and install
|
|
316
|
+
git clone https://github.com/G4brym/workers-sentinel.git
|
|
317
|
+
cd workers-sentinel
|
|
318
|
+
pnpm install
|
|
319
|
+
|
|
320
|
+
# Start development (runs wrangler dev)
|
|
321
|
+
pnpm dev
|
|
322
|
+
|
|
323
|
+
# Build dashboard
|
|
324
|
+
pnpm --filter @workers-sentinel/dashboard build
|
|
325
|
+
|
|
326
|
+
# Typecheck
|
|
327
|
+
pnpm typecheck
|
|
328
|
+
|
|
329
|
+
# Lint
|
|
330
|
+
pnpm lint
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## License
|
|
334
|
+
|
|
335
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
**Made with ❤️ for the self-hosted community**
|
|
340
|
+
|
|
341
|
+
If you find Workers Sentinel useful, please consider giving it a ⭐ on GitHub!
|
package/dist/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This folder contains the built output assets for the worker "workers-sentinel" generated at 2026-01-18T21:56:03.666Z.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as b,k as x,l as w,r,x as h,c as l,h as m,a as t,t as v,e as E,n as I,w as f,f as y,R as g,y as N,b as _,o as a}from"./index-7dRN2NZv.js";const $={key:0,class:"text-center py-12"},B={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},V={key:2},j={class:"mb-6"},C={class:"flex items-center space-x-2 text-sm text-gray-500 mb-2"},R={class:"text-xl font-bold text-gray-900 dark:text-white"},D={class:"card p-4"},S={class:"text-xs font-mono overflow-x-auto whitespace-pre-wrap text-gray-900 dark:text-gray-100"},L=b({__name:"EventDetail",setup(F){const c=w(),n=x(()=>c.params.slug),p=x(()=>c.params.eventId),u=r(null),d=r(null),i=r(!0),o=r(null);async function k(){i.value=!0,o.value=null;try{const s=await N.get(`/api/projects/${n.value}/events/${p.value}`);u.value=s.event,d.value=s.issueId}catch(s){o.value=s instanceof Error?s.message:"Failed to load event"}finally{i.value=!1}}return h(()=>k()),(s,e)=>(a(),l("div",null,[i.value?(a(),l("div",$,[...e[0]||(e[0]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1)])])):o.value?(a(),l("div",B,v(o.value),1)):u.value?(a(),l("div",V,[t("div",j,[t("div",C,[E(y(g),{to:`/projects/${n.value}/issues`,class:"hover:text-gray-700"},{default:f(()=>[...e[1]||(e[1]=[_("Issues",-1)])]),_:1},8,["to"]),e[3]||(e[3]=t("span",null,"/",-1)),d.value?(a(),I(y(g),{key:0,to:`/projects/${n.value}/issues/${d.value}`,class:"hover:text-gray-700"},{default:f(()=>[...e[2]||(e[2]=[_(" Issue ",-1)])]),_:1},8,["to"])):m("",!0),e[4]||(e[4]=t("span",null,"/",-1)),e[5]||(e[5]=t("span",null,"Event",-1))]),t("h1",R," Event "+v(p.value),1)]),t("div",D,[t("pre",S,v(JSON.stringify(u.value,null,2)),1)])])):m("",!0)]))}});export{L as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as T,k as E,l as B,r as v,x as N,c as a,h as l,a as t,t as n,e as V,w as M,f as P,R as q,b as c,q as F,F as x,p as y,y as $,o}from"./index-7dRN2NZv.js";const z={key:0,class:"text-center py-12"},A={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},O={key:2},U={class:"mb-6"},G={class:"flex items-center space-x-2 text-sm text-gray-500 mb-2"},H={class:"text-xl font-bold text-gray-900 dark:text-white mb-2"},J={key:0,class:"text-sm text-gray-500"},K={class:"flex items-center justify-between mb-6"},Q={class:"flex items-center space-x-4"},W={class:"flex items-center space-x-6 text-sm text-gray-500"},X={class:"font-medium text-gray-900 dark:text-white"},Y={key:0},Z={class:"font-medium text-gray-900 dark:text-white"},tt={class:"font-medium text-gray-900 dark:text-white"},et={key:0,class:"mb-4"},st=["value"],at=["value"],ot={key:0},nt={class:"grid grid-cols-1 lg:grid-cols-3 gap-6"},rt={class:"lg:col-span-2 space-y-4"},lt={class:"card"},it={key:0,class:"p-4 text-gray-500"},dt={key:1,class:"divide-y divide-gray-200 dark:divide-gray-700"},ut=["onClick"],ct={class:"flex items-start justify-between"},vt={class:"flex-1 min-w-0"},xt={class:"flex items-center space-x-2"},yt={key:0,class:"w-2 h-2 bg-primary-500 rounded-full",title:"In-app frame"},pt={key:1,class:"w-2 h-2 bg-gray-300 rounded-full",title:"System frame"},gt={class:"text-sm font-medium text-gray-900 dark:text-white truncate"},_t={class:"text-xs text-gray-500 mt-1 truncate"},kt={key:0},mt={key:1},ht={key:0,class:"bg-gray-900 p-4"},bt={class:"text-xs font-mono overflow-x-auto"},ft={key:1,class:"block text-white bg-error-900/50 -mx-4 px-4"},wt={key:0,class:"card"},Ct={class:"divide-y divide-gray-200 dark:divide-gray-700 max-h-96 overflow-y-auto"},It={class:"flex items-center justify-between"},$t={class:"flex items-center space-x-2"},St={class:"badge badge-info"},jt={class:"text-gray-900 dark:text-white"},Et={key:0,class:"text-xs text-gray-400"},Ft={class:"space-y-4"},Rt={class:"card p-4"},Lt={class:"space-y-2 text-sm"},Dt={class:"text-gray-900 dark:text-white font-mono text-xs"},Tt={class:"text-gray-900 dark:text-white"},Bt={key:0},Nt={class:"text-gray-900 dark:text-white"},Vt={key:1},Mt={class:"text-gray-900 dark:text-white font-mono text-xs"},Pt={key:0,class:"card p-4"},qt={class:"space-y-2 text-sm"},zt={key:0},At={class:"text-gray-900 dark:text-white font-mono text-xs"},Ot={key:1},Ut={class:"text-gray-900 dark:text-white"},Gt={key:2},Ht={class:"text-gray-900 dark:text-white font-mono"},Jt={key:1,class:"card p-4"},Kt={class:"flex flex-wrap gap-2"},Qt={class:"text-gray-500"},Wt={class:"ml-1 text-gray-900 dark:text-white"},Zt=T({__name:"IssueDetail",setup(Xt){const S=B(),k=E(()=>S.params.slug),f=E(()=>S.params.issueId),d=v(null),m=v([]),r=v(null),w=v(!0),h=v(null),p=v(new Set);async function R(){w.value=!0,h.value=null;try{const[i,e]=await Promise.all([$.get(`/api/projects/${k.value}/issues/${f.value}`),$.get(`/api/projects/${k.value}/issues/${f.value}/events?limit=10`)]);d.value=i.issue,m.value=e.events,r.value=e.events[0]||null}catch(i){h.value=i instanceof Error?i.message:"Failed to load issue"}finally{w.value=!1}}async function C(i){if(d.value)try{await $.patch(`/api/projects/${k.value}/issues/${f.value}`,{status:i}),d.value.status=i}catch(e){console.error("Failed to update status:",e)}}function I(i){return new Date(i).toLocaleString()}function L(i){p.value.has(i)?p.value.delete(i):p.value.add(i)}function j(){var e,g,s,u;const i=(s=(g=(e=r.value)==null?void 0:e.exception)==null?void 0:g.values)==null?void 0:s[0];return(u=i==null?void 0:i.stacktrace)!=null&&u.frames?[...i.stacktrace.frames].reverse():[]}function D(i){switch(i){case"fatal":case"error":return"text-error-600 dark:text-error-400";case"warning":return"text-warning-600 dark:text-warning-400";default:return"text-primary-600 dark:text-primary-400"}}return N(()=>R()),(i,e)=>{var g;return o(),a("div",null,[w.value?(o(),a("div",z,[...e[4]||(e[4]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1)])])):h.value?(o(),a("div",A,n(h.value),1)):d.value&&r.value?(o(),a("div",O,[t("div",U,[t("div",G,[V(P(q),{to:`/projects/${k.value}/issues`,class:"hover:text-gray-700"},{default:M(()=>[...e[5]||(e[5]=[c(" Issues ",-1)])]),_:1},8,["to"]),e[6]||(e[6]=t("span",null,"/",-1)),t("span",null,n(d.value.metadata.type),1)]),t("h1",H,[t("span",{class:F(D(d.value.level))},n(d.value.metadata.type)+":",3),c(" "+n(d.value.metadata.value),1)]),d.value.culprit?(o(),a("p",J,n(d.value.culprit),1)):l("",!0)]),t("div",K,[t("div",Q,[d.value.status!=="resolved"?(o(),a("button",{key:0,class:"btn btn-secondary",onClick:e[0]||(e[0]=s=>C("resolved"))}," Resolve ")):l("",!0),d.value.status!=="ignored"?(o(),a("button",{key:1,class:"btn btn-secondary",onClick:e[1]||(e[1]=s=>C("ignored"))}," Ignore ")):l("",!0),d.value.status!=="unresolved"?(o(),a("button",{key:2,class:"btn btn-secondary",onClick:e[2]||(e[2]=s=>C("unresolved"))}," Reopen ")):l("",!0)]),t("div",W,[t("div",null,[t("span",X,n(d.value.count.toLocaleString()),1),e[7]||(e[7]=c(" events ",-1))]),d.value.userCount>0?(o(),a("div",Y,[t("span",Z,n(d.value.userCount.toLocaleString()),1),e[8]||(e[8]=c(" users ",-1))])):l("",!0),t("div",null,[e[9]||(e[9]=c(" First seen ",-1)),t("span",tt,n(I(d.value.firstSeen)),1)])])]),m.value.length>1?(o(),a("div",et,[e[10]||(e[10]=t("label",{class:"label"},"Event",-1)),t("select",{class:"input w-auto",value:r.value.event_id,onChange:e[3]||(e[3]=s=>r.value=m.value.find(u=>u.event_id===s.target.value)||null)},[(o(!0),a(x,null,y(m.value,s=>(o(),a("option",{key:s.event_id,value:s.event_id},[c(n(I(s.timestamp))+" ",1),s.environment?(o(),a("span",ot," - "+n(s.environment),1)):l("",!0)],8,at))),128))],40,st)])):l("",!0),t("div",nt,[t("div",rt,[t("div",lt,[e[12]||(e[12]=t("div",{class:"p-4 border-b border-gray-200 dark:border-gray-700"},[t("h2",{class:"font-semibold text-gray-900 dark:text-white"},"Stack Trace")],-1)),j().length===0?(o(),a("div",it," No stack trace available ")):(o(),a("div",dt,[(o(!0),a(x,null,y(j(),(s,u)=>(o(),a("div",{key:u,class:"group"},[t("button",{class:"w-full p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors",onClick:b=>L(u)},[t("div",ct,[t("div",vt,[t("div",xt,[s.in_app!==!1?(o(),a("span",yt)):(o(),a("span",pt)),t("code",gt,n(s.function||"(anonymous)"),1)]),t("p",_t,[c(n(s.filename)+" ",1),s.lineno?(o(),a("span",kt,":"+n(s.lineno),1)):l("",!0),s.colno?(o(),a("span",mt,":"+n(s.colno),1)):l("",!0)])]),(o(),a("svg",{class:F(["w-5 h-5 text-gray-400 transition-transform",{"rotate-180":p.value.has(u)}]),fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[...e[11]||(e[11]=[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 9l-7 7-7-7"},null,-1)])],2))])],8,ut),p.value.has(u)&&(s.context_line||s.pre_context||s.post_context)?(o(),a("div",ht,[t("pre",bt,[s.pre_context?(o(!0),a(x,{key:0},y(s.pre_context,(b,_)=>(o(),a("code",{key:"pre-"+_,class:"block text-gray-500"},n((s.lineno||0)-s.pre_context.length+_)+" "+n(b),1))),128)):l("",!0),s.context_line?(o(),a("code",ft,n(s.lineno)+" "+n(s.context_line),1)):l("",!0),s.post_context?(o(!0),a(x,{key:2},y(s.post_context,(b,_)=>(o(),a("code",{key:"post-"+_,class:"block text-gray-500"},n((s.lineno||0)+_+1)+" "+n(b),1))),128)):l("",!0)])])):l("",!0)]))),128))]))]),(g=r.value.breadcrumbs)!=null&&g.length?(o(),a("div",wt,[e[13]||(e[13]=t("div",{class:"p-4 border-b border-gray-200 dark:border-gray-700"},[t("h2",{class:"font-semibold text-gray-900 dark:text-white"},"Breadcrumbs")],-1)),t("div",Ct,[(o(!0),a(x,null,y(r.value.breadcrumbs,(s,u)=>(o(),a("div",{key:u,class:"p-3 text-sm"},[t("div",It,[t("div",$t,[t("span",St,n(s.category||s.type||"default"),1),t("span",jt,n(s.message),1)]),s.timestamp?(o(),a("span",Et,n(new Date(s.timestamp).toLocaleTimeString()),1)):l("",!0)])]))),128))])])):l("",!0)]),t("div",Ft,[t("div",Rt,[e[18]||(e[18]=t("h3",{class:"font-semibold text-gray-900 dark:text-white mb-3"},"Event Info",-1)),t("dl",Lt,[t("div",null,[e[14]||(e[14]=t("dt",{class:"text-gray-500"},"Event ID",-1)),t("dd",Dt,n(r.value.event_id),1)]),t("div",null,[e[15]||(e[15]=t("dt",{class:"text-gray-500"},"Timestamp",-1)),t("dd",Tt,n(I(r.value.timestamp)),1)]),r.value.environment?(o(),a("div",Bt,[e[16]||(e[16]=t("dt",{class:"text-gray-500"},"Environment",-1)),t("dd",Nt,n(r.value.environment),1)])):l("",!0),r.value.release?(o(),a("div",Vt,[e[17]||(e[17]=t("dt",{class:"text-gray-500"},"Release",-1)),t("dd",Mt,n(r.value.release),1)])):l("",!0)])]),r.value.user?(o(),a("div",Pt,[e[22]||(e[22]=t("h3",{class:"font-semibold text-gray-900 dark:text-white mb-3"},"User",-1)),t("dl",qt,[r.value.user.id?(o(),a("div",zt,[e[19]||(e[19]=t("dt",{class:"text-gray-500"},"ID",-1)),t("dd",At,n(r.value.user.id),1)])):l("",!0),r.value.user.email?(o(),a("div",Ot,[e[20]||(e[20]=t("dt",{class:"text-gray-500"},"Email",-1)),t("dd",Ut,n(r.value.user.email),1)])):l("",!0),r.value.user.ip_address?(o(),a("div",Gt,[e[21]||(e[21]=t("dt",{class:"text-gray-500"},"IP Address",-1)),t("dd",Ht,n(r.value.user.ip_address),1)])):l("",!0)])])):l("",!0),r.value.tags&&Object.keys(r.value.tags).length>0?(o(),a("div",Jt,[e[23]||(e[23]=t("h3",{class:"font-semibold text-gray-900 dark:text-white mb-3"},"Tags",-1)),t("div",Kt,[(o(!0),a(x,null,y(r.value.tags,(s,u)=>(o(),a("span",{key:u,class:"inline-flex items-center px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-xs"},[t("span",Qt,n(u)+":",1),t("span",Wt,n(s),1)]))),128))])])):l("",!0)])])])):l("",!0)])}}});export{Zt as default};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import{d as N,k as h,l as R,r as i,x as E,m as b,c as a,a as s,h as c,i as I,A as P,t as n,z as S,F as C,p as T,y as k,n as U,w as B,q as F,g as z,f as A,R as V,o as r}from"./index-7dRN2NZv.js";import{u as W}from"./projects-BUj8O17i.js";const H={class:"flex items-center space-x-4 mb-6"},K={key:0,class:"text-center py-12"},Q={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},G={key:2,class:"max-w-2xl mx-auto"},J={class:"bg-gray-50 dark:bg-gray-700 rounded-lg p-6 mt-6"},O={class:"font-medium text-gray-900 dark:text-white mb-4"},X={class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},Y={key:1,class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},Z={key:3,class:"card divide-y divide-gray-200 dark:divide-gray-700"},tt={class:"flex items-start justify-between"},et={class:"flex-1 min-w-0"},st={class:"flex items-center space-x-2"},ot={class:"text-sm font-medium text-gray-900 dark:text-white truncate"},rt={class:"mt-1 text-sm text-gray-600 dark:text-gray-300 truncate"},at={key:0,class:"mt-1 text-xs text-gray-400 truncate"},nt={class:"ml-4 flex flex-col items-end text-sm"},lt={class:"flex items-center space-x-4 text-gray-500"},it={title:"Events"},ut={key:0,title:"Users"},dt={class:"text-xs text-gray-400 mt-1"},ct=["onClick"],gt=["onClick"],vt=["onClick"],pt={key:4,class:"mt-4 text-center"},xt=["disabled"],yt={key:0},mt={key:1},_t=N({__name:"Issues",setup(ft){const q=R(),$=W(),u=h(()=>q.params.slug),j=h(()=>$.projects.find(o=>o.slug===u.value)),_=h(()=>{var o;return((o=j.value)==null?void 0:o.platform)==="cloudflare-workers"}),d=i([]),g=i(!0),p=i(null),v=i("unresolved"),w=i(!1),m=i(),x=i("");async function y(o=!1){o||(g.value=!0),p.value=null;try{const e=new URLSearchParams;v.value&&e.set("status",v.value),o&&m.value&&e.set("cursor",m.value);const t=await k.get(`/api/projects/${u.value}/issues?${e}`);o?d.value=[...d.value,...t.issues]:d.value=t.issues,w.value=t.hasMore,m.value=t.nextCursor,t.issues.length===0&&!x.value&&L()}catch(e){p.value=e instanceof Error?e.message:"Failed to load issues"}finally{g.value=!1}}async function L(){try{const o=await k.get(`/api/projects/${u.value}`);x.value=o.dsn}catch{}}async function f(o,e){try{await k.patch(`/api/projects/${u.value}/issues/${o.id}`,{status:e}),o.status=e}catch(t){console.error("Failed to update status:",t)}}function D(o){const e=new Date(o),l=Math.floor((new Date().getTime()-e.getTime())/1e3);return l<60?"just now":l<3600?`${Math.floor(l/60)}m ago`:l<86400?`${Math.floor(l/3600)}h ago`:l<604800?`${Math.floor(l/86400)}d ago`:e.toLocaleDateString()}function M(o){switch(o){case"fatal":case"error":return"badge-error";case"warning":return"badge-warning";default:return"badge-info"}}return E(()=>y()),b(v,()=>y()),b(u,()=>y()),(o,e)=>(r(),a("div",null,[s("div",H,[I(s("select",{"onUpdate:modelValue":e[0]||(e[0]=t=>v.value=t),class:"input w-auto"},[...e[3]||(e[3]=[s("option",{value:""},"All Issues",-1),s("option",{value:"unresolved"},"Unresolved",-1),s("option",{value:"resolved"},"Resolved",-1),s("option",{value:"ignored"},"Ignored",-1)])],512),[[P,v.value]])]),g.value&&d.value.length===0?(r(),a("div",K,[...e[4]||(e[4]=[s("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1),s("p",{class:"mt-4 text-gray-500"},"Loading issues...",-1)])])):p.value?(r(),a("div",Q,n(p.value),1)):d.value.length===0?(r(),a("div",G,[e[7]||(e[7]=S('<div class="text-center py-8"><svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg><h3 class="mt-2 text-lg font-medium text-gray-900 dark:text-white">No issues yet</h3><p class="mt-1 text-sm text-gray-500"> Configure your SDK to start capturing errors. </p></div>',1)),s("div",J,[s("h3",O,n(_.value?"Quick Setup (Cloudflare Workers)":"Quick Setup"),1),_.value?(r(),a(C,{key:0},[e[5]||(e[5]=S(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 1. Install the SDK: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>npm install @sentry/cloudflare --save</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 2. Add a service binding to Sentinel and the compatibility flag in <code class="text-xs bg-gray-200 dark:bg-gray-600 px-1 rounded">wrangler.jsonc</code>: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>{
|
|
2
|
+
"compatibility_flags": ["nodejs_als"],
|
|
3
|
+
"services": [
|
|
4
|
+
{ "binding": "SENTINEL", "service": "workers-sentinel", "entrypoint": "SentinelRpc" }
|
|
5
|
+
]
|
|
6
|
+
}</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 3. Initialize Sentry with the RPC transport: </p>`,5)),s("pre",X,[s("code",null,`import * as Sentry from "@sentry/cloudflare";
|
|
7
|
+
import { waitUntil } from "cloudflare:workers";
|
|
8
|
+
|
|
9
|
+
const DSN = "`+n(x.value)+`";
|
|
10
|
+
|
|
11
|
+
export default Sentry.withSentry(
|
|
12
|
+
(env: Env) => ({
|
|
13
|
+
dsn: DSN,
|
|
14
|
+
transport: () => ({
|
|
15
|
+
send: async (envelope) => {
|
|
16
|
+
const rpcPromise = env.SENTINEL.captureEnvelope(DSN, envelope);
|
|
17
|
+
waitUntil(rpcPromise);
|
|
18
|
+
const result = await rpcPromise;
|
|
19
|
+
return { statusCode: result.status };
|
|
20
|
+
},
|
|
21
|
+
flush: async () => true,
|
|
22
|
+
}),
|
|
23
|
+
}),
|
|
24
|
+
{
|
|
25
|
+
async fetch(request, env, ctx) {
|
|
26
|
+
return new Response("Hello World!");
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
);`,1)]),e[6]||(e[6]=s("p",{class:"text-xs text-gray-500 dark:text-gray-400 mt-3"}," Using a service binding with RPC routes requests internally within Cloudflare's network, avoiding external HTTP roundtrips and reducing latency. ",-1))],64)):(r(),a("pre",Y,[s("code",null,`import * as Sentry from '@sentry/browser';
|
|
30
|
+
|
|
31
|
+
Sentry.init({
|
|
32
|
+
dsn: '`+n(x.value)+`',
|
|
33
|
+
});`,1)]))])])):(r(),a("div",Z,[(r(!0),a(C,null,T(d.value,t=>(r(),U(A(V),{key:t.id,to:`/projects/${u.value}/issues/${t.id}`,class:"block p-4 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"},{default:B(()=>[s("div",tt,[s("div",et,[s("div",st,[s("span",{class:F(["badge",M(t.level)])},n(t.level),3),s("h3",ot,n(t.metadata.type),1)]),s("p",rt,n(t.metadata.value||t.title),1),t.culprit?(r(),a("p",at,n(t.culprit),1)):c("",!0)]),s("div",nt,[s("div",lt,[s("span",it,n(t.count.toLocaleString()),1),t.userCount>0?(r(),a("span",ut,n(t.userCount.toLocaleString())+" users ",1)):c("",!0)]),s("p",dt,n(D(t.lastSeen)),1)])]),s("div",{class:"mt-3 flex items-center space-x-2",onClick:e[1]||(e[1]=z(()=>{},["prevent"]))},[t.status!=="resolved"?(r(),a("button",{key:0,class:"text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",onClick:l=>f(t,"resolved")}," Resolve ",8,ct)):c("",!0),t.status!=="ignored"?(r(),a("button",{key:1,class:"text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",onClick:l=>f(t,"ignored")}," Ignore ",8,gt)):c("",!0),t.status!=="unresolved"?(r(),a("button",{key:2,class:"text-xs text-gray-500 hover:text-gray-700 dark:hover:text-gray-300",onClick:l=>f(t,"unresolved")}," Reopen ",8,vt)):c("",!0)])]),_:2},1032,["to"]))),128))])),w.value?(r(),a("div",pt,[s("button",{class:"btn btn-secondary",disabled:g.value,onClick:e[2]||(e[2]=t=>y(!0))},[g.value?(r(),a("span",yt,"Loading...")):(r(),a("span",mt,"Load More"))],8,xt)])):c("",!0)]))}});export{_t as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as S,u as B,k as w,l as V,m as L,c as a,a as e,e as n,w as l,f as t,R as i,n as R,h as k,F as N,p as P,t as d,q as p,s as A,j as H,b as y,o as r}from"./index-7dRN2NZv.js";import{u as $}from"./projects-BUj8O17i.js";const z={class:"flex h-screen"},F={class:"w-64 bg-gray-900 text-white flex flex-col"},M={class:"p-4 border-b border-gray-800"},q={class:"flex-1 overflow-y-auto p-4"},D={class:"flex items-center justify-between mb-4"},E={key:0,class:"text-gray-400 text-sm"},I={key:1,class:"space-y-1"},T={class:"truncate"},U={class:"p-4 border-t border-gray-800"},G={class:"flex items-center justify-between"},J={class:"flex items-center min-w-0"},K={class:"w-8 h-8 bg-gray-700 rounded-full flex items-center justify-center text-sm font-medium"},O={class:"ml-3 min-w-0"},Q={class:"text-sm font-medium truncate"},W={class:"text-xs text-gray-400 truncate"},X={class:"flex-1 overflow-y-auto"},Y={key:0,class:"bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 px-6 py-4"},Z={class:"flex items-center justify-between"},ee={class:"flex items-center space-x-4"},te={class:"text-xl font-semibold text-gray-900 dark:text-white"},se={class:"badge badge-info"},oe={class:"flex items-center space-x-2"},re={class:"p-6"},ie=S({__name:"Layout",setup(ae){const m=V(),j=H(),c=B(),o=$(),h=w(()=>m.params.slug),u=w(()=>o.projects.find(g=>g.slug===h.value));async function C(){await c.logout(),o.reset(),j.push("/login")}return L(()=>c.isAuthenticated,g=>{g&&!o.initialized&&o.loadProjects()},{immediate:!0}),(g,s)=>{var f,_,v,b;return r(),a("div",z,[e("aside",F,[e("div",M,[n(t(i),{to:"/",class:"flex items-center space-x-2"},{default:l(()=>[...s[0]||(s[0]=[e("div",{class:"w-8 h-8 bg-primary-500 rounded-lg flex items-center justify-center"},[e("span",{class:"text-lg font-bold"},"S")],-1),e("span",{class:"font-semibold"},"Sentinel",-1)])]),_:1})]),e("nav",q,[e("div",D,[s[2]||(s[2]=e("h2",{class:"text-xs font-semibold text-gray-400 uppercase tracking-wider"},"Projects",-1)),n(t(i),{to:"/projects/new",class:"text-gray-400 hover:text-white"},{default:l(()=>[...s[1]||(s[1]=[e("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1)])]),_:1})]),t(o).loading?(r(),a("div",E,"Loading...")):(r(),a("ul",I,[(r(!0),a(N,null,P(t(o).projects,x=>(r(),a("li",{key:x.id},[n(t(i),{to:`/projects/${x.slug}/issues`,class:p(["flex items-center px-3 py-2 rounded-lg text-sm",h.value===x.slug?"bg-gray-800 text-white":"text-gray-300 hover:bg-gray-800 hover:text-white"])},{default:l(()=>[e("span",T,d(x.name),1)]),_:2},1032,["to","class"])]))),128))])),!t(o).loading&&t(o).projects.length===0?(r(),R(t(i),{key:2,to:"/projects/new",class:"block text-center py-8 text-gray-400 hover:text-white"},{default:l(()=>[...s[3]||(s[3]=[y(" Create your first project ",-1)])]),_:1})):k("",!0)]),e("div",U,[e("div",G,[e("div",J,[e("div",K,d((_=(f=t(c).user)==null?void 0:f.name)==null?void 0:_.charAt(0).toUpperCase()),1),e("div",O,[e("p",Q,d((v=t(c).user)==null?void 0:v.name),1),e("p",W,d((b=t(c).user)==null?void 0:b.email),1)])]),e("button",{onClick:C,class:"text-gray-400 hover:text-white",title:"Logout"},[...s[4]||(s[4]=[e("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"})],-1)])])])])]),e("main",X,[u.value?(r(),a("header",Y,[e("div",Z,[e("div",ee,[e("h1",te,d(u.value.name),1),e("span",se,d(u.value.platform),1)]),e("div",oe,[n(t(i),{to:`/projects/${u.value.slug}/issues`,class:p(["px-3 py-1.5 text-sm rounded-lg",t(m).name==="issues"?"bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white":"text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"])},{default:l(()=>[...s[5]||(s[5]=[y(" Issues ",-1)])]),_:1},8,["to","class"]),n(t(i),{to:`/projects/${u.value.slug}/settings`,class:p(["px-3 py-1.5 text-sm rounded-lg",t(m).name==="project-settings"?"bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-white":"text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700"])},{default:l(()=>[...s[6]||(s[6]=[y(" Settings ",-1)])]),_:1},8,["to","class"])])])])):k("",!0),e("div",re,[n(t(A))])])])}}});export{ie as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as f,u as g,r as d,c as r,a as t,b as u,e as b,w as v,f as a,R as w,g as k,h as S,t as h,i as p,v as m,j as _,o}from"./index-7dRN2NZv.js";const V={class:"min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 py-12 px-4 sm:px-6 lg:px-8"},N={class:"max-w-md w-full space-y-8"},j={class:"mt-2 text-center text-sm text-gray-600 dark:text-gray-400"},B={key:0,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg text-sm"},C={class:"space-y-4"},R=["disabled"],q={key:0},D={key:1},M=f({__name:"Login",setup(E){const c=_(),s=g(),l=d(""),n=d("");async function x(){await s.login(l.value,n.value)&&c.push("/")}return(y,e)=>(o(),r("div",V,[t("div",N,[t("div",null,[e[4]||(e[4]=t("div",{class:"mx-auto w-12 h-12 bg-primary-500 rounded-xl flex items-center justify-center"},[t("span",{class:"text-2xl font-bold text-white"},"S")],-1)),e[5]||(e[5]=t("h2",{class:"mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white"}," Sign in to Sentinel ",-1)),t("p",j,[e[3]||(e[3]=u(" Or ",-1)),b(a(w),{to:"/register",class:"font-medium text-primary-600 hover:text-primary-500"},{default:v(()=>[...e[2]||(e[2]=[u(" create a new account ",-1)])]),_:1})])]),t("form",{class:"mt-8 space-y-6",onSubmit:k(x,["prevent"])},[a(s).error?(o(),r("div",B,h(a(s).error),1)):S("",!0),t("div",C,[t("div",null,[e[6]||(e[6]=t("label",{for:"email",class:"label"},"Email address",-1)),p(t("input",{id:"email","onUpdate:modelValue":e[0]||(e[0]=i=>l.value=i),type:"email",autocomplete:"email",required:"",class:"input",placeholder:"you@example.com"},null,512),[[m,l.value]])]),t("div",null,[e[7]||(e[7]=t("label",{for:"password",class:"label"},"Password",-1)),p(t("input",{id:"password","onUpdate:modelValue":e[1]||(e[1]=i=>n.value=i),type:"password",autocomplete:"current-password",required:"",class:"input",placeholder:"••••••••"},null,512),[[m,n.value]])])]),t("button",{type:"submit",disabled:a(s).loading,class:"btn btn-primary w-full"},[a(s).loading?(o(),r("span",q,"Signing in...")):(o(),r("span",D,"Sign in"))],8,R)],32)])]))}});export{M as default};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import{d as S,r as u,k as j,c as o,a as e,t as n,F as y,z as q,g as C,h as P,i as g,v as _,A as N,p as D,f as E,j as T,y as R,o as a}from"./index-7dRN2NZv.js";import{u as M}from"./projects-BUj8O17i.js";const U={class:"max-w-2xl mx-auto"},V={key:0,class:"card p-6"},B={class:"text-center mb-6"},A={class:"text-gray-500 mt-1"},H={class:"mb-6"},I={class:"flex items-center space-x-2"},L={class:"flex-1 bg-gray-100 dark:bg-gray-700 px-3 py-2 rounded-lg text-sm font-mono break-all text-gray-900 dark:text-gray-100"},z={class:"bg-gray-50 dark:bg-gray-700 rounded-lg p-4 mb-6"},F={class:"font-medium text-gray-900 dark:text-white mb-2"},J={class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},W={key:1,class:"text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto"},G={key:0,class:"mb-4 bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg text-sm"},K={class:"space-y-4"},Q=["value"],$={class:"mt-6 flex justify-end space-x-3"},O=["disabled"],Y={key:0},X={key:1},re=S({__name:"ProjectCreate",setup(Z){const m=T(),b=M(),p=u(""),i=u("cloudflare-workers"),v=u("cloudflare-workers"),d=u(!1),c=u(null),r=u(null),x=[{value:"cloudflare-workers",label:"Cloudflare Workers"},{value:"javascript",label:"JavaScript"},{value:"node",label:"Node.js"},{value:"python",label:"Python"},{value:"go",label:"Go"},{value:"rust",label:"Rust"},{value:"java",label:"Java"},{value:"php",label:"PHP"},{value:"ruby",label:"Ruby"},{value:"other",label:"Other"}];async function f(){d.value=!0,c.value=null;try{v.value=i.value;const s=await R.post("/api/projects",{name:p.value,platform:i.value});r.value=s,b.addProject({id:s.project.id,name:s.project.name,slug:s.project.slug,platform:i.value})}catch(s){c.value=s instanceof Error?s.message:"Failed to create project"}finally{d.value=!1}}const k=j(()=>v.value==="cloudflare-workers"?"Quick Setup (Cloudflare Workers)":"Quick Setup (JavaScript)");function w(){r.value&&navigator.clipboard.writeText(r.value.dsn)}function h(){r.value&&m.push(`/projects/${r.value.project.slug}/issues`)}return(s,t)=>(a(),o("div",U,[t[12]||(t[12]=e("h1",{class:"text-2xl font-bold text-gray-900 dark:text-white mb-6"},"Create New Project",-1)),r.value?(a(),o("div",V,[e("div",B,[t[3]||(t[3]=e("div",{class:"mx-auto w-12 h-12 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center mb-4"},[e("svg",{class:"w-6 h-6 text-green-600 dark:text-green-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M5 13l4 4L19 7"})])],-1)),t[4]||(t[4]=e("h2",{class:"text-xl font-semibold text-gray-900 dark:text-white"},"Project Created!",-1)),e("p",A,n(r.value.project.name),1)]),e("div",H,[t[6]||(t[6]=e("label",{class:"label"},"Your DSN",-1)),e("div",I,[e("code",L,n(r.value.dsn),1),e("button",{onClick:w,class:"btn btn-secondary",title:"Copy to clipboard"},[...t[5]||(t[5]=[e("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[e("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})],-1)])])]),t[7]||(t[7]=e("p",{class:"mt-2 text-sm text-gray-500"}," Use this DSN to configure your Sentry SDK. ",-1))]),e("div",z,[e("h3",F,n(k.value),1),v.value==="cloudflare-workers"?(a(),o(y,{key:0},[t[8]||(t[8]=q(`<p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 1. Install the SDK: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>npm install @sentry/cloudflare --save</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 2. Add a service binding to Sentinel and the compatibility flag in <code class="text-xs bg-gray-200 dark:bg-gray-600 px-1 rounded">wrangler.jsonc</code>: </p><pre class="text-sm bg-gray-900 text-gray-100 rounded-lg p-4 overflow-x-auto mb-4"><code>{
|
|
2
|
+
"compatibility_flags": ["nodejs_als"],
|
|
3
|
+
"services": [
|
|
4
|
+
{ "binding": "SENTINEL", "service": "workers-sentinel", "entrypoint": "SentinelRpc" }
|
|
5
|
+
]
|
|
6
|
+
}</code></pre><p class="text-sm text-gray-600 dark:text-gray-400 mb-3"> 3. Initialize Sentry with the RPC transport: </p>`,5)),e("pre",J,[e("code",null,`import * as Sentry from "@sentry/cloudflare";
|
|
7
|
+
import { waitUntil } from "cloudflare:workers";
|
|
8
|
+
|
|
9
|
+
const DSN = "`+n(r.value.dsn)+`";
|
|
10
|
+
|
|
11
|
+
export default Sentry.withSentry(
|
|
12
|
+
(env: Env) => ({
|
|
13
|
+
dsn: DSN,
|
|
14
|
+
transport: () => ({
|
|
15
|
+
send: async (envelope) => {
|
|
16
|
+
const rpcPromise = env.SENTINEL.captureEnvelope(DSN, envelope);
|
|
17
|
+
waitUntil(rpcPromise);
|
|
18
|
+
const result = await rpcPromise;
|
|
19
|
+
return { statusCode: result.status };
|
|
20
|
+
},
|
|
21
|
+
flush: async () => true,
|
|
22
|
+
}),
|
|
23
|
+
}),
|
|
24
|
+
{
|
|
25
|
+
async fetch(request, env, ctx) {
|
|
26
|
+
return new Response("Hello World!");
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
);`,1)]),t[9]||(t[9]=e("p",{class:"text-xs text-gray-500 dark:text-gray-400 mt-3"}," Using a service binding with RPC routes requests internally within Cloudflare's network, avoiding external HTTP roundtrips and reducing latency. ",-1))],64)):(a(),o("pre",W,[e("code",null,`import * as Sentry from '@sentry/browser';
|
|
30
|
+
|
|
31
|
+
Sentry.init({
|
|
32
|
+
dsn: '`+n(r.value.dsn)+`',
|
|
33
|
+
});`,1)]))]),e("button",{onClick:h,class:"btn btn-primary w-full"}," Go to Project ")])):(a(),o("form",{key:1,class:"card p-6",onSubmit:C(f,["prevent"])},[c.value?(a(),o("div",G,n(c.value),1)):P("",!0),e("div",K,[e("div",null,[t[10]||(t[10]=e("label",{for:"name",class:"label"},"Project Name",-1)),g(e("input",{id:"name","onUpdate:modelValue":t[0]||(t[0]=l=>p.value=l),type:"text",required:"",class:"input",placeholder:"My Application"},null,512),[[_,p.value]])]),e("div",null,[t[11]||(t[11]=e("label",{for:"platform",class:"label"},"Platform",-1)),g(e("select",{id:"platform","onUpdate:modelValue":t[1]||(t[1]=l=>i.value=l),class:"input"},[(a(),o(y,null,D(x,l=>e("option",{key:l.value,value:l.value},n(l.label),9,Q)),64))],512),[[N,i.value]])])]),e("div",$,[e("button",{type:"button",class:"btn btn-secondary",onClick:t[2]||(t[2]=l=>E(m).back())}," Cancel "),e("button",{type:"submit",disabled:d.value,class:"btn btn-primary"},[d.value?(a(),o("span",Y,"Creating...")):(a(),o("span",X,"Create Project"))],8,O)])],32))]))}});export{re as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as _,k as D,l as C,r as n,x as S,c as a,h as v,a as t,t as r,g as P,y as p,j as N,o as l}from"./index-7dRN2NZv.js";import{u as $}from"./projects-BUj8O17i.js";const B={class:"max-w-2xl"},A={key:0,class:"text-center py-12"},E={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},M={key:2,class:"space-y-6"},V={class:"card p-6"},z={class:"flex items-center space-x-2"},F={class:"flex-1 bg-gray-100 dark:bg-gray-700 px-3 py-2 rounded-lg text-sm font-mono break-all text-gray-900 dark:text-gray-100"},I={class:"card p-6"},K={class:"space-y-4"},R={class:"text-gray-900 dark:text-white"},T={class:"text-gray-900 dark:text-white font-mono"},U={class:"text-gray-900 dark:text-white"},H={class:"text-gray-900 dark:text-white"},L={class:"text-gray-900 dark:text-white font-mono text-sm"},O={class:"card border-error-200 dark:border-error-800"},Z={class:"p-6"},q={class:"flex items-center justify-between"},G={class:"bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md mx-4"},J={class:"text-lg font-semibold text-gray-900 dark:text-white mb-2"},Q={class:"flex justify-end space-x-3"},W=["disabled"],X={key:0},Y={key:1},ot=_({__name:"ProjectSettings",setup(tt){const y=C(),b=N(),f=$(),c=D(()=>y.params.slug),o=n(null),x=n(""),m=n(!0),d=n(null),i=n(!1),u=n(!1);async function k(){m.value=!0,d.value=null;try{const s=await p.get(`/api/projects/${c.value}`);o.value=s.project,x.value=s.dsn}catch(s){d.value=s instanceof Error?s.message:"Failed to load project"}finally{m.value=!1}}async function h(){if(o.value){i.value=!0;try{await p.delete(`/api/projects/${c.value}`),f.removeProject(c.value),b.push("/projects")}catch(s){d.value=s instanceof Error?s.message:"Failed to delete project",i.value=!1}}}function j(){navigator.clipboard.writeText(x.value)}function w(s){return new Date(s).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric",hour:"2-digit",minute:"2-digit"})}return S(()=>k()),(s,e)=>(l(),a("div",B,[m.value?(l(),a("div",A,[...e[3]||(e[3]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1)])])):d.value?(l(),a("div",E,r(d.value),1)):o.value?(l(),a("div",M,[e[16]||(e[16]=t("h1",{class:"text-2xl font-bold text-gray-900 dark:text-white"},"Project Settings",-1)),t("div",V,[e[5]||(e[5]=t("h2",{class:"text-lg font-semibold text-gray-900 dark:text-white mb-4"},"Client Keys (DSN)",-1)),e[6]||(e[6]=t("p",{class:"text-sm text-gray-500 mb-4"}," Use this DSN to configure your Sentry SDK. ",-1)),t("div",z,[t("code",F,r(x.value),1),t("button",{onClick:j,class:"btn btn-secondary",title:"Copy to clipboard"},[...e[4]||(e[4]=[t("svg",{class:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"})],-1)])])])]),t("div",I,[e[12]||(e[12]=t("h2",{class:"text-lg font-semibold text-gray-900 dark:text-white mb-4"},"Project Information",-1)),t("dl",K,[t("div",null,[e[7]||(e[7]=t("dt",{class:"text-sm text-gray-500"},"Name",-1)),t("dd",R,r(o.value.name),1)]),t("div",null,[e[8]||(e[8]=t("dt",{class:"text-sm text-gray-500"},"Slug",-1)),t("dd",T,r(o.value.slug),1)]),t("div",null,[e[9]||(e[9]=t("dt",{class:"text-sm text-gray-500"},"Platform",-1)),t("dd",U,r(o.value.platform),1)]),t("div",null,[e[10]||(e[10]=t("dt",{class:"text-sm text-gray-500"},"Created",-1)),t("dd",H,r(w(o.value.createdAt)),1)]),t("div",null,[e[11]||(e[11]=t("dt",{class:"text-sm text-gray-500"},"Project ID",-1)),t("dd",L,r(o.value.id),1)])])]),t("div",O,[e[14]||(e[14]=t("div",{class:"p-6 border-b border-error-200 dark:border-error-800"},[t("h2",{class:"text-lg font-semibold text-error-600 dark:text-error-400"},"Danger Zone")],-1)),t("div",Z,[t("div",q,[e[13]||(e[13]=t("div",null,[t("h3",{class:"text-sm font-medium text-gray-900 dark:text-white"},"Delete this project"),t("p",{class:"text-sm text-gray-500"}," Once you delete a project, there is no going back. All data will be permanently deleted. ")],-1)),t("button",{class:"btn btn-danger",onClick:e[0]||(e[0]=g=>u.value=!0)}," Delete Project ")])])]),u.value?(l(),a("div",{key:0,class:"fixed inset-0 bg-black/50 flex items-center justify-center z-50",onClick:e[2]||(e[2]=P(g=>u.value=!1,["self"]))},[t("div",G,[t("h3",J,' Delete "'+r(o.value.name)+'"? ',1),e[15]||(e[15]=t("p",{class:"text-sm text-gray-500 mb-6"}," This action cannot be undone. All issues, events, and settings for this project will be permanently deleted. ",-1)),t("div",Q,[t("button",{class:"btn btn-secondary",onClick:e[1]||(e[1]=g=>u.value=!1)}," Cancel "),t("button",{class:"btn btn-danger",disabled:i.value,onClick:h},[i.value?(l(),a("span",X,"Deleting...")):(l(),a("span",Y,"Delete Project"))],8,W)])])])):v("",!0)])):v("",!0)]))}});export{ot as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as y,r as d,x as f,y as k,c as a,a as t,e as x,w as c,f as u,R as m,t as n,F as w,p as h,b as g,o as r,n as _}from"./index-7dRN2NZv.js";const b={class:"flex items-center justify-between mb-6"},j={key:0,class:"text-center py-12"},B={key:1,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg"},C={key:2,class:"text-center py-12"},M={class:"mt-6"},N={key:3,class:"grid gap-4 md:grid-cols-2 lg:grid-cols-3"},V={class:"flex items-start justify-between"},D={class:"font-semibold text-gray-900 dark:text-white"},H={class:"text-sm text-gray-500 mt-1"},L={class:"badge badge-info"},P={class:"text-xs text-gray-400 mt-4"},E=y({__name:"Projects",setup(F){const l=d([]),p=d(!0),i=d(null);f(async()=>{try{const s=await k.get("/api/projects");l.value=s.projects}catch(s){i.value=s instanceof Error?s.message:"Failed to load projects"}finally{p.value=!1}});function v(s){return new Date(s).toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"})}return(s,e)=>(r(),a("div",null,[t("div",b,[e[1]||(e[1]=t("h1",{class:"text-2xl font-bold text-gray-900 dark:text-white"},"Projects",-1)),x(u(m),{to:"/projects/new",class:"btn btn-primary"},{default:c(()=>[...e[0]||(e[0]=[t("svg",{class:"w-5 h-5 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1),g(" New Project ",-1)])]),_:1})]),p.value?(r(),a("div",j,[...e[2]||(e[2]=[t("div",{class:"animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600 mx-auto"},null,-1),t("p",{class:"mt-4 text-gray-500"},"Loading projects...",-1)])])):i.value?(r(),a("div",B,n(i.value),1)):l.value.length===0?(r(),a("div",C,[e[4]||(e[4]=t("svg",{class:"mx-auto h-12 w-12 text-gray-400",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"})],-1)),e[5]||(e[5]=t("h3",{class:"mt-2 text-sm font-medium text-gray-900 dark:text-white"},"No projects",-1)),e[6]||(e[6]=t("p",{class:"mt-1 text-sm text-gray-500"},"Get started by creating a new project.",-1)),t("div",M,[x(u(m),{to:"/projects/new",class:"btn btn-primary"},{default:c(()=>[...e[3]||(e[3]=[t("svg",{class:"w-5 h-5 mr-2",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24"},[t("path",{"stroke-linecap":"round","stroke-linejoin":"round","stroke-width":"2",d:"M12 4v16m8-8H4"})],-1),g(" New Project ",-1)])]),_:1})])])):(r(),a("div",N,[(r(!0),a(w,null,h(l.value,o=>(r(),_(u(m),{key:o.id,to:`/projects/${o.slug}/issues`,class:"card p-6 hover:shadow-md transition-shadow"},{default:c(()=>[t("div",V,[t("div",null,[t("h3",D,n(o.name),1),t("p",H,n(o.slug),1)]),t("span",L,n(o.platform),1)]),t("p",P,"Created "+n(v(o.createdAt)),1)]),_:2},1032,["to"]))),128))]))]))}});export{E as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{d as v,u as g,r as u,c as l,a as t,b as x,e as b,w,f as r,R as k,g as h,h as S,t as V,i as p,v as m,j as C,o}from"./index-7dRN2NZv.js";const N={class:"min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 py-12 px-4 sm:px-6 lg:px-8"},R={class:"max-w-md w-full space-y-8"},j={class:"mt-2 text-center text-sm text-gray-600 dark:text-gray-400"},q={key:0,class:"bg-error-50 dark:bg-error-900/20 text-error-700 dark:text-error-400 px-4 py-3 rounded-lg text-sm"},A={class:"space-y-4"},B=["disabled"],D={key:0},U={key:1},M=v({__name:"Register",setup(_){const c=C(),s=g(),n=u(""),i=u(""),d=u("");async function y(){await s.register(i.value,d.value,n.value)&&c.push("/")}return(f,e)=>(o(),l("div",N,[t("div",R,[t("div",null,[e[5]||(e[5]=t("div",{class:"mx-auto w-12 h-12 bg-primary-500 rounded-xl flex items-center justify-center"},[t("span",{class:"text-2xl font-bold text-white"},"S")],-1)),e[6]||(e[6]=t("h2",{class:"mt-6 text-center text-3xl font-extrabold text-gray-900 dark:text-white"}," Create your account ",-1)),t("p",j,[e[4]||(e[4]=x(" Already have an account? ",-1)),b(r(k),{to:"/login",class:"font-medium text-primary-600 hover:text-primary-500"},{default:w(()=>[...e[3]||(e[3]=[x(" Sign in ",-1)])]),_:1})])]),t("form",{class:"mt-8 space-y-6",onSubmit:h(y,["prevent"])},[r(s).error?(o(),l("div",q,V(r(s).error),1)):S("",!0),t("div",A,[t("div",null,[e[7]||(e[7]=t("label",{for:"name",class:"label"},"Name",-1)),p(t("input",{id:"name","onUpdate:modelValue":e[0]||(e[0]=a=>n.value=a),type:"text",autocomplete:"name",required:"",class:"input",placeholder:"John Doe"},null,512),[[m,n.value]])]),t("div",null,[e[8]||(e[8]=t("label",{for:"email",class:"label"},"Email address",-1)),p(t("input",{id:"email","onUpdate:modelValue":e[1]||(e[1]=a=>i.value=a),type:"email",autocomplete:"email",required:"",class:"input",placeholder:"you@example.com"},null,512),[[m,i.value]])]),t("div",null,[e[9]||(e[9]=t("label",{for:"password",class:"label"},"Password",-1)),p(t("input",{id:"password","onUpdate:modelValue":e[2]||(e[2]=a=>d.value=a),type:"password",autocomplete:"new-password",required:"",minlength:"8",class:"input",placeholder:"••••••••"},null,512),[[m,d.value]]),e[10]||(e[10]=t("p",{class:"mt-1 text-xs text-gray-500"},"At least 8 characters",-1))])]),t("button",{type:"submit",disabled:r(s).loading,class:"btn btn-primary w-full"},[r(s).loading?(o(),l("span",D,"Creating account...")):(o(),l("span",U,"Create account"))],8,B)],32)])]))}});export{M as default};
|