Compare commits
4 commits
main
...
claude/fin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d40abd593 | ||
|
|
a1ff55c41d | ||
|
|
543080003b | ||
|
|
77a2fa68c6 |
12 changed files with 2201 additions and 30 deletions
|
|
@ -30,7 +30,7 @@ export default function SEO({
|
|||
noIndex,
|
||||
}: SEOProps) {
|
||||
useEffect(() => {
|
||||
const title = `AeThex | ${pageTitle}`;
|
||||
const title = `[DEV] AeThex | ${pageTitle}`;
|
||||
document.title = title;
|
||||
|
||||
if (canonical) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState } from "react";
|
||||
import DOMPurify from "dompurify";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
|
|
@ -269,7 +270,7 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
{excerpt && (
|
||||
<p className="text-muted-foreground italic">{excerpt}</p>
|
||||
)}
|
||||
<div dangerouslySetInnerHTML={{ __html: html }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import DOMPurify from "dompurify";
|
||||
import Layout from "@/components/Layout";
|
||||
import SEO from "@/components/SEO";
|
||||
import {
|
||||
|
|
@ -135,7 +136,7 @@ export default function BlogPost() {
|
|||
</CardHeader>
|
||||
<CardContent className="prose max-w-none mt-6">
|
||||
{post.body ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: post.body }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(post.body) }} />
|
||||
) : (
|
||||
<p>{post.excerpt}</p>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -192,11 +192,27 @@ export default function Onboarding() {
|
|||
};
|
||||
let nextStep = 0;
|
||||
|
||||
// Do not restore from localStorage; clear any legacy key
|
||||
// Restore from localStorage if available (allows users to continue where they left off)
|
||||
if (typeof window !== "undefined") {
|
||||
try {
|
||||
const saved = window.localStorage.getItem(ONBOARDING_STORAGE_KEY);
|
||||
if (saved) {
|
||||
const { currentStep: savedStep, data: savedData, timestamp } = JSON.parse(saved);
|
||||
// Only restore if saved within last 7 days
|
||||
const MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days in ms
|
||||
if (timestamp && Date.now() - timestamp < MAX_AGE) {
|
||||
nextData = savedData;
|
||||
nextStep = savedStep;
|
||||
} else {
|
||||
// Clear expired data
|
||||
window.localStorage.removeItem(ONBOARDING_STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Failed to restore onboarding progress:", error);
|
||||
// Clear corrupted data
|
||||
window.localStorage.removeItem(ONBOARDING_STORAGE_KEY);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
if (user?.id) {
|
||||
|
|
@ -235,13 +251,20 @@ export default function Onboarding() {
|
|||
};
|
||||
}, [user, steps.length, mapProfileToOnboardingData]);
|
||||
|
||||
// Save onboarding progress to localStorage after each change
|
||||
useEffect(() => {
|
||||
// Disable local persistence for onboarding (but not while finishing)
|
||||
if (typeof window === "undefined" || isFinishing) return;
|
||||
if (typeof window === "undefined" || !hydrated || isFinishing) return;
|
||||
try {
|
||||
window.localStorage.removeItem(ONBOARDING_STORAGE_KEY);
|
||||
} catch {}
|
||||
}, [hydrated, isFinishing]);
|
||||
const progressData = {
|
||||
currentStep,
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
window.localStorage.setItem(ONBOARDING_STORAGE_KEY, JSON.stringify(progressData));
|
||||
} catch (error) {
|
||||
console.warn("Failed to save onboarding progress:", error);
|
||||
}
|
||||
}, [currentStep, data, hydrated, isFinishing, ONBOARDING_STORAGE_KEY]);
|
||||
|
||||
const updateData = useCallback((newData: Partial<OnboardingData>) => {
|
||||
setData((prev) => ({
|
||||
|
|
|
|||
544
docs/issues/P0-ISSUES.md
Normal file
544
docs/issues/P0-ISSUES.md
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
# P0 Priority Issues (Fix ASAP)
|
||||
|
||||
These are critical issues that should be addressed immediately.
|
||||
|
||||
---
|
||||
|
||||
## Issue 1: [P0] Fix onboarding progress loss on page refresh
|
||||
|
||||
**Labels:** `bug`, `P0`, `onboarding`, `user-experience`
|
||||
|
||||
### Problem
|
||||
Users lose all onboarding progress when they refresh the page during the multi-step onboarding flow. This leads to high dropout rates and poor user experience.
|
||||
|
||||
### Current Behavior
|
||||
- User starts onboarding at `/onboarding`
|
||||
- User completes steps 1-4 of 8-step wizard
|
||||
- User refreshes page (accidentally or intentionally)
|
||||
- All progress is lost, user starts from step 1 again
|
||||
|
||||
### Root Cause
|
||||
Session storage persistence was removed from `client/pages/Onboarding.tsx:238-243`. Progress is now only stored in memory (React state), which is lost on page refresh.
|
||||
|
||||
### Expected Behavior
|
||||
- Progress should be saved to sessionStorage after each step
|
||||
- On page load, check sessionStorage for existing progress
|
||||
- If found, restore user to their last completed step
|
||||
- Clear sessionStorage only after successful onboarding completion
|
||||
|
||||
### Technical Details
|
||||
- **File:** `client/pages/Onboarding.tsx`
|
||||
- **Lines:** 238-243 (where persistence was removed)
|
||||
- **State to persist:** `currentStep`, `formData`, `userType`
|
||||
|
||||
### Implementation Approach
|
||||
1. Re-enable sessionStorage persistence with key like `aethex_onboarding_progress`
|
||||
2. Save state after each step completion
|
||||
3. On component mount, check for existing progress
|
||||
4. Restore state if found
|
||||
5. Add recovery UI: "Continue where you left off?" prompt
|
||||
6. Clear storage on successful completion
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Progress persists across page refreshes
|
||||
- [ ] User can resume from last completed step
|
||||
- [ ] Storage is cleared after onboarding completes
|
||||
- [ ] Works across browser tabs (same session)
|
||||
- [ ] Handles edge cases (corrupted data, version changes)
|
||||
|
||||
### Impact
|
||||
- **High onboarding dropout rate**
|
||||
- **Poor first-time user experience**
|
||||
- **Lost user data and effort**
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: [P0] Complete Stripe payment integration
|
||||
|
||||
**Labels:** `bug`, `P0`, `payments`, `backend`, `stripe`
|
||||
|
||||
### Problem
|
||||
The payment flow UI exists but Stripe integration may not be fully implemented. Users cannot upgrade to Pro/Council tiers, preventing monetization.
|
||||
|
||||
### Current Behavior
|
||||
- Pricing page at `/pricing` shows subscription tiers
|
||||
- "Upgrade" buttons exist for Pro ($9/mo) and Council ($29/mo)
|
||||
- Clicking upgrade attempts to call `/api/subscriptions/create-checkout`
|
||||
- Integration status unknown - needs verification
|
||||
|
||||
### Missing/Incomplete Features
|
||||
1. **Stripe Checkout Session Creation**
|
||||
- Verify `/api/subscriptions/create-checkout` endpoint works
|
||||
- Ensure proper price IDs are configured
|
||||
- Test redirect flow
|
||||
|
||||
2. **Webhook Handlers**
|
||||
- `checkout.session.completed` - Create subscription in DB
|
||||
- `customer.subscription.updated` - Update tier changes
|
||||
- `customer.subscription.deleted` - Handle cancellations
|
||||
- `invoice.payment_failed` - Handle failed payments
|
||||
|
||||
3. **Billing Portal**
|
||||
- "Manage Subscription" button should open Stripe portal
|
||||
- Verify `/api/subscriptions/manage` endpoint
|
||||
- Test cancellation and plan changes
|
||||
|
||||
4. **Database Sync**
|
||||
- Update `profiles.tier` on successful payment
|
||||
- Track subscription status in DB
|
||||
- Store Stripe customer ID and subscription ID
|
||||
|
||||
### Technical Details
|
||||
- **Files:**
|
||||
- `client/pages/Pricing.tsx` (UI)
|
||||
- `server/index.ts` or `api/subscriptions/*` (backend)
|
||||
- Stripe webhooks configuration
|
||||
- **Environment Variables Needed:**
|
||||
- `STRIPE_SECRET_KEY`
|
||||
- `STRIPE_PUBLISHABLE_KEY`
|
||||
- `STRIPE_WEBHOOK_SECRET`
|
||||
- Price IDs for Pro and Council tiers
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Verify Stripe API keys are configured
|
||||
- [ ] Create/verify product and price IDs in Stripe
|
||||
- [ ] Implement create-checkout endpoint
|
||||
- [ ] Implement manage-subscription endpoint
|
||||
- [ ] Set up webhook endpoint
|
||||
- [ ] Add webhook handlers for all events
|
||||
- [ ] Test full checkout flow
|
||||
- [ ] Test subscription management flow
|
||||
- [ ] Test cancellation flow
|
||||
- [ ] Add error handling and retry logic
|
||||
|
||||
### Testing Scenarios
|
||||
1. New user upgrades from Free → Pro
|
||||
2. Pro user upgrades to Council
|
||||
3. Council user cancels subscription
|
||||
4. Payment fails, retry logic
|
||||
5. User changes payment method
|
||||
6. User views billing history
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Users can successfully upgrade to Pro tier
|
||||
- [ ] Users can successfully upgrade to Council tier
|
||||
- [ ] Subscription status syncs to database
|
||||
- [ ] Billing portal opens and works correctly
|
||||
- [ ] Webhooks properly handle all events
|
||||
- [ ] Failed payments are handled gracefully
|
||||
- [ ] Users can cancel subscriptions
|
||||
|
||||
### Impact
|
||||
- **Cannot monetize the platform**
|
||||
- **No paid tier access for users**
|
||||
- **Revenue loss**
|
||||
|
||||
---
|
||||
|
||||
## Issue 3: [P0] Refactor large components (Feed, ProfilePassport, AuthContext)
|
||||
|
||||
**Labels:** `tech-debt`, `P0`, `refactoring`, `performance`
|
||||
|
||||
### Problem
|
||||
Several components exceed 900+ lines, making them difficult to maintain, test, and debug. This increases bug risk and slows development velocity.
|
||||
|
||||
### Affected Components
|
||||
|
||||
#### 1. AuthContext.tsx (1,246 lines)
|
||||
**Location:** `client/contexts/AuthContext.tsx`
|
||||
|
||||
**Current Responsibilities:** (Too many)
|
||||
- Session management
|
||||
- OAuth flow handling
|
||||
- Email resolution logic
|
||||
- Profile fetching
|
||||
- Multiple provider linking
|
||||
- Error handling
|
||||
- Loading states
|
||||
- Storage clearing
|
||||
|
||||
**Refactoring Approach:**
|
||||
- Split into multiple contexts:
|
||||
- `AuthContext` - Core auth state only
|
||||
- `SessionContext` - Session management
|
||||
- `OAuthContext` - OAuth providers
|
||||
- `ProfileContext` - User profile data
|
||||
- Extract services:
|
||||
- `authService.ts` - Auth API calls
|
||||
- `sessionService.ts` - Session operations
|
||||
- `oauthService.ts` - OAuth flows
|
||||
|
||||
#### 2. Feed.tsx (958 lines)
|
||||
**Location:** `client/pages/Feed.tsx`
|
||||
|
||||
**Current Responsibilities:**
|
||||
- Feed filtering logic
|
||||
- Post rendering
|
||||
- Comment handling
|
||||
- Like/unlike logic
|
||||
- Follow/unfollow logic
|
||||
- Trending topics
|
||||
- Sidebar widgets
|
||||
- Infinite scroll
|
||||
|
||||
**Refactoring Approach:**
|
||||
- Extract components:
|
||||
- `FeedFilters.tsx` - Filter bar
|
||||
- `FeedList.tsx` - Post list with infinite scroll
|
||||
- `FeedSidebar.tsx` - Sidebar widgets
|
||||
- `TrendingTopics.tsx` - Trending widget
|
||||
- `SuggestedCreators.tsx` - Suggested follows
|
||||
- Extract hooks:
|
||||
- `useFeedPosts.ts` - Post fetching logic
|
||||
- `useFeedFilters.ts` - Filter state
|
||||
- `usePostInteractions.ts` - Like/comment logic
|
||||
|
||||
#### 3. ProfilePassport.tsx (916 lines)
|
||||
**Location:** `client/pages/ProfilePassport.tsx`
|
||||
|
||||
**Current Responsibilities:**
|
||||
- Profile data fetching
|
||||
- Achievements display
|
||||
- Projects showcase
|
||||
- Social connections
|
||||
- Following/followers
|
||||
- Ethos Guild integration
|
||||
- Arm affiliations
|
||||
- Degree of connection calculation
|
||||
|
||||
**Refactoring Approach:**
|
||||
- Extract components:
|
||||
- `PassportHeader.tsx` - Avatar, name, bio
|
||||
- `PassportStats.tsx` - XP, level, streak
|
||||
- `PassportAchievements.tsx` - Badges/achievements
|
||||
- `PassportProjects.tsx` - Project showcase
|
||||
- `PassportSocial.tsx` - Followers/following
|
||||
- `PassportConnections.tsx` - Connection graph
|
||||
- Extract hooks:
|
||||
- `usePassportData.ts` - Profile fetching
|
||||
- `useConnectionDegree.ts` - Connection calculation
|
||||
|
||||
### Benefits of Refactoring
|
||||
- **Easier maintenance** - Smaller files easier to understand
|
||||
- **Better testability** - Can test components in isolation
|
||||
- **Improved performance** - Better code splitting opportunities
|
||||
- **Faster development** - Changes are localized
|
||||
- **Reduced bugs** - Less complexity = fewer edge cases
|
||||
|
||||
### Implementation Strategy
|
||||
1. Start with AuthContext (highest impact)
|
||||
2. Create new smaller contexts/services
|
||||
3. Migrate logic incrementally
|
||||
4. Maintain backward compatibility during migration
|
||||
5. Add tests for each new component
|
||||
6. Remove old code once fully migrated
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] AuthContext split into 4 smaller contexts
|
||||
- [ ] Feed.tsx split into 6+ components
|
||||
- [ ] ProfilePassport.tsx split into 7+ components
|
||||
- [ ] All functionality still works
|
||||
- [ ] No regressions in user experience
|
||||
- [ ] Code coverage maintained or improved
|
||||
|
||||
### Impact
|
||||
- **Slower development velocity**
|
||||
- **Higher bug risk**
|
||||
- **Difficult onboarding for new developers**
|
||||
- **Poor code maintainability**
|
||||
|
||||
---
|
||||
|
||||
## Issue 4: [P0] Add comprehensive error tracking (Sentry integration)
|
||||
|
||||
**Labels:** `infrastructure`, `P0`, `monitoring`, `error-tracking`
|
||||
|
||||
### Problem
|
||||
Errors are currently logged to console but not tracked or monitored. This means:
|
||||
- Production errors go unnoticed
|
||||
- No visibility into user-impacting issues
|
||||
- Difficult to debug production problems
|
||||
- No error analytics or trends
|
||||
|
||||
### Current Error Handling
|
||||
- `console.error()` scattered throughout codebase
|
||||
- Toast notifications for some errors
|
||||
- No centralized error tracking
|
||||
- No error grouping or deduplication
|
||||
- No user context attached to errors
|
||||
|
||||
### Proposed Solution
|
||||
Integrate Sentry for comprehensive error tracking and monitoring.
|
||||
|
||||
### Implementation Checklist
|
||||
|
||||
#### 1. Install Dependencies
|
||||
```bash
|
||||
npm install @sentry/react @sentry/tracing
|
||||
```
|
||||
|
||||
#### 2. Initialize Sentry
|
||||
**File:** `client/main.tsx`
|
||||
```typescript
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { BrowserTracing } from "@sentry/tracing";
|
||||
|
||||
Sentry.init({
|
||||
dsn: import.meta.env.VITE_SENTRY_DSN,
|
||||
environment: import.meta.env.MODE,
|
||||
integrations: [new BrowserTracing()],
|
||||
tracesSampleRate: 1.0,
|
||||
beforeSend(event, hint) {
|
||||
// Filter out dev environment if needed
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
return null;
|
||||
}
|
||||
return event;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### 3. Add Error Boundary
|
||||
Wrap app with Sentry ErrorBoundary:
|
||||
```typescript
|
||||
<Sentry.ErrorBoundary fallback={<ErrorFallback />}>
|
||||
<App />
|
||||
</Sentry.ErrorBoundary>
|
||||
```
|
||||
|
||||
#### 4. Set User Context
|
||||
In AuthContext, add:
|
||||
```typescript
|
||||
Sentry.setUser({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
username: user.username,
|
||||
});
|
||||
```
|
||||
|
||||
#### 5. Add Breadcrumbs
|
||||
Track important user actions:
|
||||
```typescript
|
||||
Sentry.addBreadcrumb({
|
||||
category: 'navigation',
|
||||
message: 'Navigated to profile',
|
||||
level: 'info',
|
||||
});
|
||||
```
|
||||
|
||||
#### 6. Capture Exceptions
|
||||
Replace console.error with:
|
||||
```typescript
|
||||
try {
|
||||
// risky operation
|
||||
} catch (error) {
|
||||
Sentry.captureException(error, {
|
||||
tags: { component: 'Onboarding' },
|
||||
extra: { step: currentStep },
|
||||
});
|
||||
toast.error('Something went wrong');
|
||||
}
|
||||
```
|
||||
|
||||
### Features to Enable
|
||||
- [x] Error tracking
|
||||
- [x] Performance monitoring
|
||||
- [x] User feedback widget
|
||||
- [x] Release tracking
|
||||
- [x] Source maps upload
|
||||
- [x] Session replay (optional)
|
||||
|
||||
### Configuration Required
|
||||
- **Environment Variable:** `VITE_SENTRY_DSN`
|
||||
- **Sentry Project:** Create project at sentry.io
|
||||
- **Source Maps:** Configure upload in build process
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Sentry SDK installed and initialized
|
||||
- [ ] All uncaught errors tracked
|
||||
- [ ] User context attached to errors
|
||||
- [ ] Important actions logged as breadcrumbs
|
||||
- [ ] Source maps uploaded for readable stack traces
|
||||
- [ ] Error alerts configured
|
||||
- [ ] Team can view errors in Sentry dashboard
|
||||
|
||||
### Impact
|
||||
- **Unknown production error rate**
|
||||
- **Difficult debugging**
|
||||
- **Poor user experience** (users encounter bugs we don't know about)
|
||||
- **No data-driven prioritization** of bugs
|
||||
|
||||
---
|
||||
|
||||
## Issue 5: [P0] Add input validation to all forms
|
||||
|
||||
**Labels:** `bug`, `P0`, `forms`, `validation`, `user-experience`
|
||||
|
||||
### Problem
|
||||
Forms lack comprehensive client-side validation, leading to:
|
||||
- Poor user experience (errors only shown after submission)
|
||||
- Invalid data sent to API
|
||||
- Confusing error messages
|
||||
- Higher API error rates
|
||||
|
||||
### Affected Forms
|
||||
|
||||
#### 1. Onboarding Forms
|
||||
**Files:** `client/components/onboarding/*.tsx`
|
||||
|
||||
**Missing Validation:**
|
||||
- **PersonalInfo:**
|
||||
- Name: Required, min 2 chars
|
||||
- Email: Valid email format, required
|
||||
- Company: Optional, max 100 chars
|
||||
- **Experience:**
|
||||
- Skills: Required, at least 1 skill
|
||||
- Experience level: Required
|
||||
- **Interests:**
|
||||
- Primary goals: Required, at least 1
|
||||
- **RealmSelection:**
|
||||
- Realm: Required selection
|
||||
|
||||
#### 2. Profile Editing
|
||||
**File:** `client/pages/Dashboard.tsx` (Profile tab)
|
||||
|
||||
**Missing Validation:**
|
||||
- Display name: Required, 2-50 chars
|
||||
- Bio: Optional, max 500 chars
|
||||
- Website URL: Valid URL format
|
||||
- Social links: Valid URL formats
|
||||
- GitHub: Valid GitHub username format
|
||||
|
||||
#### 3. Authentication Forms
|
||||
**File:** `client/pages/Login.tsx`
|
||||
|
||||
**Missing Validation:**
|
||||
- Email: Required, valid format
|
||||
- Password: Required, min 6 chars (current), should be min 8 chars
|
||||
- Password confirmation: Must match password
|
||||
|
||||
#### 4. Post Composer
|
||||
**File:** `client/components/feed/PostComposer.tsx`
|
||||
|
||||
**Missing Validation:**
|
||||
- Content: Required, min 1 char, max 5000 chars
|
||||
- Image URL: Valid URL if provided
|
||||
- Links: Valid URL format
|
||||
|
||||
### Recommended Validation Library
|
||||
Use **React Hook Form** + **Zod** for type-safe validation:
|
||||
|
||||
```bash
|
||||
npm install react-hook-form zod @hookform/resolvers
|
||||
```
|
||||
|
||||
### Implementation Example
|
||||
|
||||
#### Define Schema (Zod)
|
||||
```typescript
|
||||
// schemas/onboarding.ts
|
||||
import { z } from 'zod';
|
||||
|
||||
export const personalInfoSchema = z.object({
|
||||
name: z.string().min(2, 'Name must be at least 2 characters').max(50),
|
||||
email: z.string().email('Invalid email address'),
|
||||
company: z.string().max(100).optional(),
|
||||
});
|
||||
|
||||
export type PersonalInfoFormData = z.infer<typeof personalInfoSchema>;
|
||||
```
|
||||
|
||||
#### Use in Component
|
||||
```typescript
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { personalInfoSchema } from '@/schemas/onboarding';
|
||||
|
||||
function PersonalInfo() {
|
||||
const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
resolver: zodResolver(personalInfoSchema),
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
// Data is validated
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<input {...register('name')} />
|
||||
{errors.name && <span>{errors.name.message}</span>}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Validation Rules by Field Type
|
||||
|
||||
#### Email
|
||||
- Required
|
||||
- Valid email format (`/^[^\s@]+@[^\s@]+\.[^\s@]+$/`)
|
||||
- Max 255 chars
|
||||
|
||||
#### Password
|
||||
- Required
|
||||
- Min 8 chars (update from current 6)
|
||||
- Must contain: uppercase, lowercase, number
|
||||
- Max 128 chars
|
||||
|
||||
#### URLs
|
||||
- Valid URL format
|
||||
- Must start with http:// or https://
|
||||
- Optional fields can be empty
|
||||
|
||||
#### Text Fields
|
||||
- Trim whitespace
|
||||
- Min/max length based on field
|
||||
- No HTML injection (sanitize)
|
||||
|
||||
#### Usernames
|
||||
- Alphanumeric + underscores/hyphens
|
||||
- 3-20 characters
|
||||
- Not reserved words
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Install react-hook-form and zod
|
||||
- [ ] Create validation schemas in `/client/schemas/`
|
||||
- [ ] Update PersonalInfo component
|
||||
- [ ] Update Experience component
|
||||
- [ ] Update Interests component
|
||||
- [ ] Update RealmSelection component
|
||||
- [ ] Update Profile editing form
|
||||
- [ ] Update Login/Signup forms
|
||||
- [ ] Update Post composer
|
||||
- [ ] Add real-time validation feedback
|
||||
- [ ] Add field-level error messages
|
||||
- [ ] Add form-level error summary
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] All forms have client-side validation
|
||||
- [ ] Errors shown in real-time (on blur)
|
||||
- [ ] Clear, actionable error messages
|
||||
- [ ] Form submission disabled until valid
|
||||
- [ ] Visual indicators for invalid fields
|
||||
- [ ] Success indicators for valid fields
|
||||
- [ ] Validation schemas are reusable
|
||||
- [ ] Type-safe form data
|
||||
|
||||
### Impact
|
||||
- **Poor user experience**
|
||||
- **Higher API error rates**
|
||||
- **Confusing error messages**
|
||||
- **Wasted server resources** (invalid requests)
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
These P0 issues should be addressed in this order:
|
||||
1. **Onboarding progress persistence** (30 min fix, high user impact)
|
||||
2. **Error tracking** (1 hour setup, critical for monitoring)
|
||||
3. **Form validation** (1-2 days, improves UX across app)
|
||||
4. **Stripe integration** (2-3 days, enables monetization)
|
||||
5. **Component refactoring** (1-2 weeks, ongoing improvement)
|
||||
|
||||
Total estimated effort: **2-3 weeks** for all P0 items.
|
||||
654
docs/issues/P1-ISSUES.md
Normal file
654
docs/issues/P1-ISSUES.md
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
# P1 Priority Issues (Medium Priority)
|
||||
|
||||
These issues should be addressed after P0 items are complete.
|
||||
|
||||
---
|
||||
|
||||
## Issue 1: [P1] Build comprehensive notification system
|
||||
|
||||
**Labels:** `feature`, `P1`, `notifications`, `engagement`
|
||||
|
||||
### Problem
|
||||
Users have no way to know when someone:
|
||||
- Likes their post
|
||||
- Comments on their post
|
||||
- Follows them
|
||||
- Mentions them
|
||||
- Awards them an achievement
|
||||
|
||||
This leads to poor engagement and missed interactions.
|
||||
|
||||
### Proposed Features
|
||||
|
||||
#### 1. In-App Notifications
|
||||
- Notification bell icon in header
|
||||
- Badge with unread count
|
||||
- Dropdown panel with recent notifications
|
||||
- Mark as read functionality
|
||||
- Clear all functionality
|
||||
|
||||
#### 2. Notification Types
|
||||
- **Social:**
|
||||
- New follower
|
||||
- Post liked
|
||||
- Post commented
|
||||
- Mention in post/comment
|
||||
- **Achievement:**
|
||||
- Achievement unlocked
|
||||
- Level up
|
||||
- Streak milestone
|
||||
- **System:**
|
||||
- Welcome message
|
||||
- Onboarding completion
|
||||
- Subscription renewal
|
||||
- Payment issues
|
||||
|
||||
#### 3. Email Notifications
|
||||
- Daily digest of activity
|
||||
- Weekly summary
|
||||
- Immediate alerts for important events
|
||||
- Unsubscribe options
|
||||
|
||||
#### 4. Push Notifications (PWA)
|
||||
- Browser push for real-time alerts
|
||||
- Permission request UI
|
||||
- Notification preferences
|
||||
|
||||
### Technical Implementation
|
||||
|
||||
#### Database Schema
|
||||
```sql
|
||||
CREATE TABLE notifications (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES profiles(id),
|
||||
type VARCHAR(50), -- 'follow', 'like', 'comment', etc.
|
||||
title VARCHAR(255),
|
||||
body TEXT,
|
||||
link VARCHAR(500), -- Where to go when clicked
|
||||
read BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
metadata JSONB -- Extra data specific to notification type
|
||||
);
|
||||
|
||||
CREATE INDEX idx_notifications_user_read ON notifications(user_id, read);
|
||||
CREATE INDEX idx_notifications_created ON notifications(created_at);
|
||||
```
|
||||
|
||||
#### API Endpoints
|
||||
- `GET /api/notifications` - Fetch user's notifications
|
||||
- `POST /api/notifications/:id/read` - Mark as read
|
||||
- `POST /api/notifications/read-all` - Mark all as read
|
||||
- `DELETE /api/notifications/:id` - Delete notification
|
||||
- `GET /api/notifications/unread-count` - Get count
|
||||
|
||||
#### Real-time Updates
|
||||
Use Supabase Realtime subscriptions:
|
||||
```typescript
|
||||
const subscription = supabase
|
||||
.channel('notifications')
|
||||
.on('postgres_changes', {
|
||||
event: 'INSERT',
|
||||
schema: 'public',
|
||||
table: 'notifications',
|
||||
filter: `user_id=eq.${userId}`
|
||||
}, handleNewNotification)
|
||||
.subscribe();
|
||||
```
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Create notifications table
|
||||
- [ ] Create NotificationContext
|
||||
- [ ] Build NotificationBell component
|
||||
- [ ] Build NotificationPanel component
|
||||
- [ ] Add API endpoints
|
||||
- [ ] Integrate Supabase Realtime
|
||||
- [ ] Add notification triggers (on like, comment, etc.)
|
||||
- [ ] Build email notification service
|
||||
- [ ] Add user preferences UI
|
||||
- [ ] Add push notification support
|
||||
- [ ] Test all notification types
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Users receive in-app notifications
|
||||
- [ ] Unread count displays in header
|
||||
- [ ] Clicking notification navigates to relevant content
|
||||
- [ ] Users can mark notifications as read
|
||||
- [ ] Users can configure notification preferences
|
||||
- [ ] Email notifications sent for key events
|
||||
- [ ] Real-time updates without page refresh
|
||||
|
||||
### Estimated Effort
|
||||
**2-3 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: [P1] Complete project management workflows
|
||||
|
||||
**Labels:** `feature`, `P1`, `projects`, `collaboration`
|
||||
|
||||
### Problem
|
||||
Project features are incomplete:
|
||||
- ProjectBoard.tsx has TODO comment on line 1
|
||||
- Project creation appears to be a stub
|
||||
- No team collaboration features
|
||||
- No file management
|
||||
|
||||
### Missing Features
|
||||
|
||||
#### 1. Project Creation
|
||||
**File:** `client/pages/ProjectsNew.tsx`
|
||||
|
||||
**Needs:**
|
||||
- Full project creation form
|
||||
- Project templates (game dev, web app, etc.)
|
||||
- Team member invitation
|
||||
- Initial milestone setup
|
||||
- File upload/attachment
|
||||
|
||||
#### 2. Project Board (Kanban)
|
||||
**File:** `client/pages/ProjectBoard.tsx`
|
||||
|
||||
**Needs:**
|
||||
- Drag-and-drop task cards
|
||||
- Columns: Backlog, To Do, In Progress, Review, Done
|
||||
- Task assignment
|
||||
- Due dates
|
||||
- Labels/tags
|
||||
- Comments on tasks
|
||||
- File attachments
|
||||
|
||||
#### 3. Team Collaboration
|
||||
**New Features:**
|
||||
- Multi-user projects
|
||||
- Role management (owner, editor, viewer)
|
||||
- Activity feed per project
|
||||
- @mentions in comments
|
||||
- Real-time collaboration
|
||||
- Presence indicators
|
||||
|
||||
#### 4. Project Settings
|
||||
- Edit project details
|
||||
- Manage team members
|
||||
- Archive/delete project
|
||||
- Privacy settings (public/private/team-only)
|
||||
|
||||
### Database Schema Updates
|
||||
|
||||
#### Projects Table
|
||||
```sql
|
||||
CREATE TABLE projects (
|
||||
id UUID PRIMARY KEY,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
owner_id UUID REFERENCES profiles(id),
|
||||
status VARCHAR(50), -- 'active', 'archived', 'completed'
|
||||
visibility VARCHAR(20), -- 'public', 'private', 'team'
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
#### Project Members
|
||||
```sql
|
||||
CREATE TABLE project_members (
|
||||
project_id UUID REFERENCES projects(id),
|
||||
user_id UUID REFERENCES profiles(id),
|
||||
role VARCHAR(20), -- 'owner', 'editor', 'viewer'
|
||||
joined_at TIMESTAMP DEFAULT NOW(),
|
||||
PRIMARY KEY (project_id, user_id)
|
||||
);
|
||||
```
|
||||
|
||||
#### Project Tasks
|
||||
```sql
|
||||
CREATE TABLE project_tasks (
|
||||
id UUID PRIMARY KEY,
|
||||
project_id UUID REFERENCES projects(id),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
status VARCHAR(50), -- 'backlog', 'todo', 'in_progress', 'review', 'done'
|
||||
assigned_to UUID REFERENCES profiles(id),
|
||||
due_date DATE,
|
||||
priority VARCHAR(20), -- 'low', 'medium', 'high', 'urgent'
|
||||
position INTEGER, -- For ordering within column
|
||||
created_by UUID REFERENCES profiles(id),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Complete ProjectsNew form
|
||||
- [ ] Implement project creation API
|
||||
- [ ] Build Kanban board with drag-and-drop
|
||||
- [ ] Add task CRUD operations
|
||||
- [ ] Implement team member management
|
||||
- [ ] Add comments system for tasks
|
||||
- [ ] Add file attachment support
|
||||
- [ ] Implement project activity feed
|
||||
- [ ] Add real-time collaboration
|
||||
- [ ] Build project settings page
|
||||
- [ ] Add project templates
|
||||
- [ ] Implement project search/filter
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Users can create projects
|
||||
- [ ] Users can manage tasks on Kanban board
|
||||
- [ ] Users can invite team members
|
||||
- [ ] Team members can collaborate in real-time
|
||||
- [ ] Users can upload files to projects
|
||||
- [ ] Users can comment on tasks
|
||||
- [ ] Users can archive/delete projects
|
||||
|
||||
### Estimated Effort
|
||||
**3-4 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 3: [P1] Add image upload functionality
|
||||
|
||||
**Labels:** `feature`, `P1`, `media`, `storage`
|
||||
|
||||
### Problem
|
||||
Users cannot upload images for:
|
||||
- Profile avatars
|
||||
- Post media
|
||||
- Project screenshots
|
||||
- Comments/attachments
|
||||
|
||||
### Proposed Solution
|
||||
Integrate Supabase Storage for image uploads.
|
||||
|
||||
### Features Needed
|
||||
|
||||
#### 1. Avatar Upload
|
||||
**Location:** Profile editing page
|
||||
|
||||
**Features:**
|
||||
- Click avatar to upload new image
|
||||
- Crop/resize before upload
|
||||
- Preview before saving
|
||||
- Remove avatar option
|
||||
- Support JPG, PNG, WebP
|
||||
- Max 5MB file size
|
||||
|
||||
#### 2. Post Media
|
||||
**Location:** Feed post composer
|
||||
|
||||
**Features:**
|
||||
- Upload 1-10 images per post
|
||||
- Drag-and-drop upload
|
||||
- Image preview with remove option
|
||||
- Automatic compression
|
||||
- Progress indicator
|
||||
|
||||
#### 3. Project Media
|
||||
**Location:** Project showcase/board
|
||||
|
||||
**Features:**
|
||||
- Project cover image
|
||||
- Screenshot gallery
|
||||
- File attachments (not just images)
|
||||
- Organize in folders
|
||||
|
||||
### Technical Implementation
|
||||
|
||||
#### Storage Buckets
|
||||
```typescript
|
||||
// Create buckets in Supabase
|
||||
- avatars (public)
|
||||
- post-media (public)
|
||||
- project-files (private with RLS)
|
||||
- attachments (private with RLS)
|
||||
```
|
||||
|
||||
#### Upload Component
|
||||
```typescript
|
||||
// components/ImageUpload.tsx
|
||||
import { supabase } from '@/lib/supabase';
|
||||
|
||||
function ImageUpload({ bucket, onUpload }) {
|
||||
const handleUpload = async (file: File) => {
|
||||
// 1. Validate file (type, size)
|
||||
// 2. Compress if needed
|
||||
// 3. Generate unique filename
|
||||
// 4. Upload to Supabase Storage
|
||||
// 5. Get public URL
|
||||
// 6. Call onUpload with URL
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### Image Compression
|
||||
```bash
|
||||
npm install browser-image-compression
|
||||
```
|
||||
|
||||
```typescript
|
||||
import imageCompression from 'browser-image-compression';
|
||||
|
||||
const options = {
|
||||
maxSizeMB: 1,
|
||||
maxWidthOrHeight: 1920,
|
||||
useWebWorker: true
|
||||
};
|
||||
|
||||
const compressedFile = await imageCompression(file, options);
|
||||
```
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Create Supabase storage buckets
|
||||
- [ ] Configure RLS policies
|
||||
- [ ] Build ImageUpload component
|
||||
- [ ] Add image compression
|
||||
- [ ] Implement avatar upload in Profile
|
||||
- [ ] Implement media upload in PostComposer
|
||||
- [ ] Implement cover image in Projects
|
||||
- [ ] Add image preview/lightbox
|
||||
- [ ] Add delete image functionality
|
||||
- [ ] Add progress indicators
|
||||
- [ ] Handle upload errors gracefully
|
||||
- [ ] Add drag-and-drop support
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Users can upload profile avatars
|
||||
- [ ] Users can upload images in posts
|
||||
- [ ] Users can upload project media
|
||||
- [ ] Images are compressed automatically
|
||||
- [ ] Upload progress is visible
|
||||
- [ ] Errors are handled gracefully
|
||||
- [ ] Uploaded images can be deleted
|
||||
|
||||
### Estimated Effort
|
||||
**1-2 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 4: [P1] Implement content moderation tools
|
||||
|
||||
**Labels:** `feature`, `P1`, `moderation`, `safety`
|
||||
|
||||
### Problem
|
||||
No way to moderate user-generated content:
|
||||
- Inappropriate posts
|
||||
- Spam
|
||||
- Harassment
|
||||
- Copyright violations
|
||||
|
||||
### Proposed Features
|
||||
|
||||
#### 1. Report System
|
||||
**User Actions:**
|
||||
- Report post
|
||||
- Report comment
|
||||
- Report user
|
||||
- Report reason categories
|
||||
- Additional details text field
|
||||
|
||||
#### 2. Admin Moderation Dashboard
|
||||
**Location:** `/staff/moderation` (requires admin role)
|
||||
|
||||
**Features:**
|
||||
- Queue of reported content
|
||||
- Review reports
|
||||
- Take actions:
|
||||
- Approve (dismiss report)
|
||||
- Remove content
|
||||
- Warn user
|
||||
- Suspend user
|
||||
- Ban user
|
||||
- View user's history
|
||||
- Bulk actions
|
||||
|
||||
#### 3. Automated Filters
|
||||
- Profanity filter (optional, toggle per community)
|
||||
- Link spam detection
|
||||
- Image content scanning (AI-based)
|
||||
- Rate limiting on posts/comments
|
||||
|
||||
#### 4. User Controls
|
||||
- Block users
|
||||
- Mute users
|
||||
- Hide posts
|
||||
- Privacy settings
|
||||
|
||||
### Database Schema
|
||||
|
||||
#### Reports Table
|
||||
```sql
|
||||
CREATE TABLE content_reports (
|
||||
id UUID PRIMARY KEY,
|
||||
reporter_id UUID REFERENCES profiles(id),
|
||||
reported_user_id UUID REFERENCES profiles(id),
|
||||
content_type VARCHAR(20), -- 'post', 'comment', 'user'
|
||||
content_id UUID,
|
||||
reason VARCHAR(50), -- 'spam', 'harassment', 'inappropriate', 'copyright'
|
||||
details TEXT,
|
||||
status VARCHAR(20), -- 'pending', 'reviewed', 'actioned', 'dismissed'
|
||||
reviewed_by UUID REFERENCES profiles(id),
|
||||
reviewed_at TIMESTAMP,
|
||||
action_taken VARCHAR(50),
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
#### User Moderation Actions
|
||||
```sql
|
||||
CREATE TABLE moderation_actions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES profiles(id),
|
||||
moderator_id UUID REFERENCES profiles(id),
|
||||
action VARCHAR(50), -- 'warn', 'suspend', 'ban', 'content_removed'
|
||||
reason TEXT,
|
||||
expires_at TIMESTAMP, -- For temporary suspensions
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
#### Blocked Users
|
||||
```sql
|
||||
CREATE TABLE user_blocks (
|
||||
blocker_id UUID REFERENCES profiles(id),
|
||||
blocked_id UUID REFERENCES profiles(id),
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
PRIMARY KEY (blocker_id, blocked_id)
|
||||
);
|
||||
```
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Create report system tables
|
||||
- [ ] Build ReportModal component
|
||||
- [ ] Add "Report" buttons to posts/comments
|
||||
- [ ] Create moderation dashboard page
|
||||
- [ ] Build report queue UI
|
||||
- [ ] Add moderation action buttons
|
||||
- [ ] Implement user blocking
|
||||
- [ ] Add automated filters
|
||||
- [ ] Create moderation logs
|
||||
- [ ] Add appeal system
|
||||
- [ ] Set up email notifications for moderators
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Users can report inappropriate content
|
||||
- [ ] Admins can review reports
|
||||
- [ ] Admins can take moderation actions
|
||||
- [ ] Users can block other users
|
||||
- [ ] Spam is automatically filtered
|
||||
- [ ] Moderation actions are logged
|
||||
- [ ] Users receive feedback on reports
|
||||
|
||||
### Estimated Effort
|
||||
**2-3 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 5: [P1] Add session management and security improvements
|
||||
|
||||
**Labels:** `security`, `P1`, `authentication`
|
||||
|
||||
### Problem
|
||||
Missing security features:
|
||||
- No 2FA (two-factor authentication)
|
||||
- No session management UI
|
||||
- No login history
|
||||
- No account recovery options
|
||||
- Weak password requirements
|
||||
|
||||
### Proposed Features
|
||||
|
||||
#### 1. Two-Factor Authentication (2FA)
|
||||
- **Setup Flow:**
|
||||
- Navigate to Settings → Security
|
||||
- Choose 2FA method (TOTP app or SMS)
|
||||
- Scan QR code (for TOTP)
|
||||
- Enter verification code
|
||||
- Save backup codes
|
||||
|
||||
- **Login Flow:**
|
||||
- Enter email/password
|
||||
- If 2FA enabled, prompt for code
|
||||
- Enter 6-digit code
|
||||
- Option to "Trust this device"
|
||||
|
||||
- **Recovery:**
|
||||
- Use backup codes
|
||||
- SMS fallback
|
||||
- Contact support
|
||||
|
||||
#### 2. Session Management
|
||||
**Location:** Settings → Security → Active Sessions
|
||||
|
||||
**Display:**
|
||||
- Current session (highlighted)
|
||||
- Other active sessions:
|
||||
- Device/browser
|
||||
- IP address
|
||||
- Location (city)
|
||||
- Last active timestamp
|
||||
- "Revoke" button for each
|
||||
|
||||
#### 3. Login History
|
||||
**Location:** Settings → Security → Login History
|
||||
|
||||
**Display:**
|
||||
- Last 30 login attempts
|
||||
- Date/time
|
||||
- Device/browser
|
||||
- IP address
|
||||
- Location
|
||||
- Success/failure status
|
||||
|
||||
#### 4. Password Strength Requirements
|
||||
Update from current 6 chars to:
|
||||
- Minimum 8 characters
|
||||
- At least one uppercase letter
|
||||
- At least one lowercase letter
|
||||
- At least one number
|
||||
- At least one special character
|
||||
- Visual strength indicator
|
||||
- Password breach check (Have I Been Pwned API)
|
||||
|
||||
#### 5. Account Recovery
|
||||
- Security questions
|
||||
- Recovery email (separate from login email)
|
||||
- SMS verification
|
||||
- Backup codes
|
||||
|
||||
### Technical Implementation
|
||||
|
||||
#### 2FA with TOTP
|
||||
```bash
|
||||
npm install otplib qrcode
|
||||
```
|
||||
|
||||
```typescript
|
||||
// Generate secret
|
||||
import { authenticator } from 'otplib';
|
||||
const secret = authenticator.generateSecret();
|
||||
|
||||
// Generate QR code
|
||||
import QRCode from 'qrcode';
|
||||
const otpauth = authenticator.keyuri(user.email, 'AeThex', secret);
|
||||
const qrcode = await QRCode.toDataURL(otpauth);
|
||||
|
||||
// Verify code
|
||||
const isValid = authenticator.verify({ token: userCode, secret });
|
||||
```
|
||||
|
||||
#### Database Schema
|
||||
```sql
|
||||
-- Add to profiles table
|
||||
ALTER TABLE profiles ADD COLUMN two_factor_enabled BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE profiles ADD COLUMN two_factor_secret VARCHAR(255);
|
||||
ALTER TABLE profiles ADD COLUMN backup_codes TEXT[]; -- Array of hashed codes
|
||||
|
||||
-- Sessions table
|
||||
CREATE TABLE user_sessions (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES profiles(id),
|
||||
token_hash VARCHAR(255),
|
||||
device VARCHAR(255),
|
||||
browser VARCHAR(100),
|
||||
ip_address INET,
|
||||
location VARCHAR(255),
|
||||
trusted BOOLEAN DEFAULT FALSE,
|
||||
created_at TIMESTAMP DEFAULT NOW(),
|
||||
last_active TIMESTAMP DEFAULT NOW(),
|
||||
expires_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- Login history
|
||||
CREATE TABLE login_history (
|
||||
id UUID PRIMARY KEY,
|
||||
user_id UUID REFERENCES profiles(id),
|
||||
success BOOLEAN,
|
||||
ip_address INET,
|
||||
device VARCHAR(255),
|
||||
browser VARCHAR(100),
|
||||
location VARCHAR(255),
|
||||
failure_reason VARCHAR(100),
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
### Implementation Checklist
|
||||
- [ ] Install 2FA dependencies
|
||||
- [ ] Create 2FA setup UI
|
||||
- [ ] Implement TOTP generation and verification
|
||||
- [ ] Add 2FA to login flow
|
||||
- [ ] Generate backup codes
|
||||
- [ ] Build session management UI
|
||||
- [ ] Track active sessions
|
||||
- [ ] Implement session revocation
|
||||
- [ ] Build login history page
|
||||
- [ ] Log all login attempts
|
||||
- [ ] Update password requirements
|
||||
- [ ] Add password strength indicator
|
||||
- [ ] Implement breach check
|
||||
- [ ] Add account recovery options
|
||||
- [ ] Add "trusted device" feature
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] Users can enable 2FA
|
||||
- [ ] 2FA required on login if enabled
|
||||
- [ ] Users can view active sessions
|
||||
- [ ] Users can revoke sessions
|
||||
- [ ] Login history is tracked
|
||||
- [ ] Password requirements enforced
|
||||
- [ ] Account recovery works
|
||||
|
||||
### Estimated Effort
|
||||
**3-4 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
P1 issues in order of priority:
|
||||
1. **Notification system** (improves engagement)
|
||||
2. **Project workflows** (core feature completion)
|
||||
3. **Image upload** (enables rich content)
|
||||
4. **Content moderation** (platform safety)
|
||||
5. **Session management & 2FA** (security)
|
||||
|
||||
Total estimated effort: **11-16 weeks**
|
||||
324
docs/issues/P2-ISSUES.md
Normal file
324
docs/issues/P2-ISSUES.md
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
# P2 Priority Issues (Low Priority / Nice to Have)
|
||||
|
||||
These are enhancement requests and quality-of-life improvements that can be addressed after P0 and P1 items.
|
||||
|
||||
---
|
||||
|
||||
## Issue 1: [P2] Add dark/light mode toggle
|
||||
|
||||
**Labels:** `enhancement`, `P2`, `UI/UX`
|
||||
|
||||
### Problem
|
||||
Theme is currently locked to dark mode. Some users prefer light mode or system preference.
|
||||
|
||||
### Proposed Solution
|
||||
- Theme toggle in header or settings
|
||||
- Persist preference in localStorage
|
||||
- Respect system preference by default
|
||||
- Smooth theme transitions
|
||||
|
||||
### Implementation
|
||||
```typescript
|
||||
// Use next-themes or custom context
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
|
||||
// In App.tsx
|
||||
<ThemeProvider attribute="class" defaultTheme="system">
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
```
|
||||
|
||||
### Estimated Effort
|
||||
**3-5 days**
|
||||
|
||||
---
|
||||
|
||||
## Issue 2: [P2] Internationalization (i18n) support
|
||||
|
||||
**Labels:** `enhancement`, `P2`, `i18n`
|
||||
|
||||
### Problem
|
||||
App is English-only. Limits international user base.
|
||||
|
||||
### Proposed Solution
|
||||
- Use react-i18next
|
||||
- Support languages: English, Spanish, French, Japanese
|
||||
- Locale switcher in settings
|
||||
- Detect browser language
|
||||
|
||||
### Estimated Effort
|
||||
**2-3 weeks** (including translations)
|
||||
|
||||
---
|
||||
|
||||
## Issue 3: [P2] Add keyboard shortcuts for power users
|
||||
|
||||
**Labels:** `enhancement`, `P2`, `accessibility`
|
||||
|
||||
### Proposed Shortcuts
|
||||
- `/` - Focus search
|
||||
- `n` - New post
|
||||
- `?` - Show shortcuts modal
|
||||
- `g h` - Go to home
|
||||
- `g p` - Go to profile
|
||||
- `Esc` - Close modals
|
||||
|
||||
### Implementation
|
||||
```bash
|
||||
npm install react-hotkeys-hook
|
||||
```
|
||||
|
||||
### Estimated Effort
|
||||
**1 week**
|
||||
|
||||
---
|
||||
|
||||
## Issue 4: [P2] Progressive Web App (PWA) support
|
||||
|
||||
**Labels:** `enhancement`, `P2`, `mobile`
|
||||
|
||||
### Features
|
||||
- Install prompt
|
||||
- Offline support
|
||||
- App icons
|
||||
- Splash screens
|
||||
- Push notifications
|
||||
|
||||
### Implementation
|
||||
Use Vite PWA plugin:
|
||||
```bash
|
||||
npm install vite-plugin-pwa
|
||||
```
|
||||
|
||||
### Estimated Effort
|
||||
**1-2 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 5: [P2] Advanced analytics dashboard
|
||||
|
||||
**Labels:** `enhancement`, `P2`, `analytics`
|
||||
|
||||
### User Analytics
|
||||
- Profile views
|
||||
- Post engagement rate
|
||||
- Follower growth
|
||||
- Top performing posts
|
||||
- Audience demographics
|
||||
|
||||
### Admin Analytics
|
||||
- Daily/weekly/monthly active users
|
||||
- User retention rates
|
||||
- Feature adoption
|
||||
- Revenue metrics
|
||||
- Funnel analysis
|
||||
|
||||
### Estimated Effort
|
||||
**2-3 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 6: [P2] Add profile customization themes
|
||||
|
||||
**Labels:** `enhancement`, `P2`, `personalization`
|
||||
|
||||
### Features
|
||||
- Custom profile colors
|
||||
- Background images
|
||||
- Layout options
|
||||
- Badge placement
|
||||
- Bio formatting (markdown)
|
||||
|
||||
### Estimated Effort
|
||||
**1-2 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 7: [P2] Implement direct messaging (DM)
|
||||
|
||||
**Labels:** `feature`, `P2`, `messaging`
|
||||
|
||||
### Features
|
||||
- One-on-one messaging
|
||||
- Message threads
|
||||
- Typing indicators
|
||||
- Read receipts
|
||||
- File sharing in DMs
|
||||
- Message search
|
||||
|
||||
### Estimated Effort
|
||||
**3-4 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 8: [P2] Add content bookmarking/saving
|
||||
|
||||
**Labels:** `feature`, `P2`, `social`
|
||||
|
||||
### Features
|
||||
- Save posts for later
|
||||
- Organize saved posts into collections
|
||||
- Private bookmarks
|
||||
- Search saved content
|
||||
|
||||
### Estimated Effort
|
||||
**1 week**
|
||||
|
||||
---
|
||||
|
||||
## Issue 9: [P2] Implement polls and reactions
|
||||
|
||||
**Labels:** `feature`, `P2`, `social`
|
||||
|
||||
### Features
|
||||
- Create polls in posts
|
||||
- Vote on polls
|
||||
- See poll results
|
||||
- Emoji reactions beyond likes
|
||||
- Reaction counts
|
||||
|
||||
### Estimated Effort
|
||||
**1-2 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 10: [P2] Add accessibility improvements
|
||||
|
||||
**Labels:** `accessibility`, `P2`, `a11y`
|
||||
|
||||
### Improvements
|
||||
- ARIA labels on all interactive elements
|
||||
- Keyboard navigation
|
||||
- Screen reader support
|
||||
- Focus indicators
|
||||
- Color contrast compliance (WCAG AA)
|
||||
- Alt text for images
|
||||
- Skip to content link
|
||||
- Reduced motion option
|
||||
|
||||
### Estimated Effort
|
||||
**2-3 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 11: [P2] Performance optimizations
|
||||
|
||||
**Labels:** `performance`, `P2`, `optimization`
|
||||
|
||||
### Optimizations
|
||||
- Route-based code splitting
|
||||
- Image lazy loading
|
||||
- Virtual scrolling for long lists
|
||||
- Bundle size reduction
|
||||
- Memoization audit
|
||||
- Remove unused dependencies
|
||||
- Lighthouse score improvements
|
||||
|
||||
### Estimated Effort
|
||||
**2-3 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 12: [P2] Add comprehensive testing
|
||||
|
||||
**Labels:** `testing`, `P2`, `quality`
|
||||
|
||||
### Testing Strategy
|
||||
- **Unit Tests:**
|
||||
- Utility functions
|
||||
- Hooks
|
||||
- Services
|
||||
|
||||
- **Component Tests:**
|
||||
- React Testing Library
|
||||
- User interactions
|
||||
- Edge cases
|
||||
|
||||
- **Integration Tests:**
|
||||
- API flows
|
||||
- Authentication
|
||||
- Critical user journeys
|
||||
|
||||
- **E2E Tests:**
|
||||
- Playwright or Cypress
|
||||
- Happy paths
|
||||
- Error scenarios
|
||||
|
||||
### Target Coverage
|
||||
- 80% code coverage
|
||||
- All critical paths tested
|
||||
- CI/CD integration
|
||||
|
||||
### Estimated Effort
|
||||
**4-6 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 13: [P2] Mobile app (React Native)
|
||||
|
||||
**Labels:** `feature`, `P2`, `mobile`
|
||||
|
||||
### Features
|
||||
- Native iOS and Android apps
|
||||
- Shared codebase with web (Expo)
|
||||
- Push notifications
|
||||
- Biometric authentication
|
||||
- Camera integration
|
||||
- App store deployment
|
||||
|
||||
### Estimated Effort
|
||||
**8-12 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 14: [P2] Add export/import user data (GDPR)
|
||||
|
||||
**Labels:** `compliance`, `P2`, `privacy`
|
||||
|
||||
### Features
|
||||
- Export all user data (JSON/CSV)
|
||||
- Import data from other platforms
|
||||
- Delete all user data
|
||||
- Data portability compliance
|
||||
- Privacy policy integration
|
||||
|
||||
### Estimated Effort
|
||||
**1-2 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Issue 15: [P2] Implement referral program
|
||||
|
||||
**Labels:** `growth`, `P2`, `monetization`
|
||||
|
||||
### Features
|
||||
- Unique referral links
|
||||
- Track referrals
|
||||
- Reward system (credits, XP, badges)
|
||||
- Leaderboard
|
||||
- Share on social media
|
||||
|
||||
### Estimated Effort
|
||||
**1-2 weeks**
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
P2 issues are nice-to-have enhancements that improve the user experience but aren't critical for core functionality. They can be addressed based on user feedback and business priorities.
|
||||
|
||||
**Quick wins** (1-2 weeks):
|
||||
- Dark mode toggle
|
||||
- Keyboard shortcuts
|
||||
- PWA support
|
||||
- Bookmarking
|
||||
- Referral program
|
||||
|
||||
**Longer term** (3+ weeks):
|
||||
- i18n support
|
||||
- Analytics dashboard
|
||||
- DM system
|
||||
- Mobile app
|
||||
- Comprehensive testing
|
||||
|
||||
Total estimated effort for all P2: **40-60 weeks** (could be distributed across multiple developers)
|
||||
40
index.html
40
index.html
|
|
@ -4,23 +4,23 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>AeThex | Developer Platform for Builders, Creators & Innovation</title>
|
||||
<title>[DEV] AeThex | Development Environment</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="AeThex: an advanced development platform and community for builders. Collaborate on projects, learn, and ship innovation."
|
||||
content="Development instance of AeThex platform - Not for production use. Internal testing and development environment."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="AeThex, developer platform, projects, community, mentorship, research labs, consulting, tutorials"
|
||||
content="AeThex, development, testing, dev environment"
|
||||
/>
|
||||
<meta name="application-name" content="AeThex" />
|
||||
<meta name="application-name" content="[DEV] AeThex" />
|
||||
<meta name="theme-color" content="#0a0aff" />
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<meta
|
||||
name="robots"
|
||||
content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
|
||||
content="noindex, nofollow"
|
||||
/>
|
||||
<meta name="googlebot" content="index, follow" />
|
||||
<meta name="googlebot" content="noindex, nofollow" />
|
||||
|
||||
<!-- Geo/Audience -->
|
||||
<meta name="geo.placename" content="Worldwide" />
|
||||
|
|
@ -57,15 +57,15 @@
|
|||
<meta name="msapplication-TileColor" content="#0a0aff" />
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:site_name" content="AeThex" />
|
||||
<meta property="og:site_name" content="[DEV] AeThex" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta
|
||||
property="og:title"
|
||||
content="AeThex — Developer Platform, Projects, Community"
|
||||
content="[DEV] AeThex — Development Environment"
|
||||
/>
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Join AeThex to build, learn, and connect. Tutorials, mentorship, research labs, and a thriving developer community."
|
||||
content="Development instance of AeThex platform - Not for production use. Internal testing and development environment."
|
||||
/>
|
||||
<meta
|
||||
property="og:image"
|
||||
|
|
@ -79,11 +79,11 @@
|
|||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta
|
||||
name="twitter:title"
|
||||
content="AeThex — Developer Platform, Projects, Community"
|
||||
content="[DEV] AeThex — Development Environment"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="Build and innovate with AeThex. Projects, mentorship, research labs, and tutorials for modern developers."
|
||||
content="Development instance of AeThex platform - Not for production use. Internal testing and development environment."
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
|
|
@ -112,8 +112,8 @@
|
|||
addJSONLD({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: "AeThex",
|
||||
legalName: "AeThex Corporation",
|
||||
name: "[DEV] AeThex",
|
||||
legalName: "AeThex Corporation (Development Instance)",
|
||||
url: origin,
|
||||
logo: "https://docs.aethex.tech/~gitbook/image?url=https%3A%2F%2F1143808467-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Forganizations%252FDhUg3jal6kdpG645FzIl%252Fsites%252Fsite_HeOmR%252Flogo%252FqxDYz8Oj2SnwUTa8t3UB%252FAeThex%2520Origin%2520logo.png%3Falt%3Dmedia%26token%3D200e8ea2-0129-4cbe-b516-4a53f60c512b&width=512&dpr=1&quality=100&sign=6c7576ce&sv=2",
|
||||
areaServed: "Worldwide",
|
||||
|
|
@ -137,7 +137,7 @@
|
|||
addJSONLD({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
name: "AeThex",
|
||||
name: "[DEV] AeThex Development Environment",
|
||||
url: origin,
|
||||
});
|
||||
// FAQ for AEO
|
||||
|
|
@ -147,26 +147,26 @@
|
|||
mainEntity: [
|
||||
{
|
||||
"@type": "Question",
|
||||
name: "What is AeThex?",
|
||||
name: "What is this site?",
|
||||
acceptedAnswer: {
|
||||
"@type": "Answer",
|
||||
text: "AeThex is an advanced development platform and community where developers collaborate on projects, learn through tutorials, and access mentorship and research labs.",
|
||||
text: "This is the development instance of AeThex platform. It is not for production use and is used for internal testing and development.",
|
||||
},
|
||||
},
|
||||
{
|
||||
"@type": "Question",
|
||||
name: "How do I get started?",
|
||||
name: "Can I use this for production?",
|
||||
acceptedAnswer: {
|
||||
"@type": "Answer",
|
||||
text: "Visit the Get Started and Onboarding flows to create your profile and join projects.",
|
||||
text: "No. This is a development environment only. Data may be reset at any time and features are experimental.",
|
||||
},
|
||||
},
|
||||
{
|
||||
"@type": "Question",
|
||||
name: "Does AeThex offer mentorship programs?",
|
||||
name: "Where is the production site?",
|
||||
acceptedAnswer: {
|
||||
"@type": "Answer",
|
||||
text: "Yes. AeThex provides mentorship programs and a community feed to help you grow and connect.",
|
||||
text: "Visit aethex.dev for the production AeThex platform.",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
211
package-lock.json
generated
211
package-lock.json
generated
|
|
@ -11,10 +11,12 @@
|
|||
"@builder.io/react": "^8.2.8",
|
||||
"@discord/embedded-app-sdk": "^2.4.0",
|
||||
"@google/genai": "^1.31.0",
|
||||
"@octokit/rest": "^22.0.1",
|
||||
"@supabase/supabase-js": "^2.53.0",
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"adm-zip": "^0.5.16",
|
||||
"chokidar": "^3.6.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"dotenv": "^17.2.0",
|
||||
"ethers": "^6.13.0",
|
||||
"express": "^4.18.2",
|
||||
|
|
@ -59,6 +61,7 @@
|
|||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/nodemailer": "^7.0.3",
|
||||
|
|
@ -3212,6 +3215,160 @@
|
|||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
|
||||
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
|
||||
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.3",
|
||||
"@octokit/request": "^10.0.6",
|
||||
"@octokit/request-error": "^7.0.2",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"before-after-hook": "^4.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz",
|
||||
"integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
|
||||
"integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^10.0.6",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"universal-user-agent": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "27.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
|
||||
"integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
|
||||
"integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-request-log": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz",
|
||||
"integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz",
|
||||
"integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "10.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz",
|
||||
"integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^11.0.2",
|
||||
"@octokit/request-error": "^7.0.2",
|
||||
"@octokit/types": "^16.0.0",
|
||||
"fast-content-type-parse": "^3.0.0",
|
||||
"universal-user-agent": "^7.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
|
||||
"integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^16.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/rest": {
|
||||
"version": "22.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz",
|
||||
"integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/core": "^7.0.6",
|
||||
"@octokit/plugin-paginate-rest": "^14.0.0",
|
||||
"@octokit/plugin-request-log": "^6.0.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^17.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
|
||||
"integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^27.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
|
@ -6881,6 +7038,16 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/dompurify": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
|
||||
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/trusted-types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/draco3d": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
||||
|
|
@ -7167,6 +7334,13 @@
|
|||
"meshoptimizer": "~0.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
|
|
@ -8111,6 +8285,12 @@
|
|||
"baseline-browser-mapping": "dist/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
|
||||
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
|
|
@ -9853,6 +10033,15 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.2.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
||||
|
|
@ -10951,6 +11140,22 @@
|
|||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/fast-content-type-parse": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
|
||||
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
|
|
@ -16548,6 +16753,12 @@
|
|||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
|
||||
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
|
|
|
|||
|
|
@ -35,10 +35,12 @@
|
|||
"@builder.io/react": "^8.2.8",
|
||||
"@discord/embedded-app-sdk": "^2.4.0",
|
||||
"@google/genai": "^1.31.0",
|
||||
"@octokit/rest": "^22.0.1",
|
||||
"@supabase/supabase-js": "^2.53.0",
|
||||
"@vercel/analytics": "^1.5.0",
|
||||
"adm-zip": "^0.5.16",
|
||||
"chokidar": "^3.6.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"dotenv": "^17.2.0",
|
||||
"ethers": "^6.13.0",
|
||||
"express": "^4.18.2",
|
||||
|
|
@ -83,6 +85,7 @@
|
|||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/nodemailer": "^7.0.3",
|
||||
|
|
|
|||
200
scripts/README.md
Normal file
200
scripts/README.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# GitHub Issues Import Script
|
||||
|
||||
Automatically converts markdown issue documentation into GitHub Issues.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install Dependencies
|
||||
|
||||
```bash
|
||||
npm install @octokit/rest
|
||||
```
|
||||
|
||||
### 2. Get GitHub Token
|
||||
|
||||
1. Go to https://github.com/settings/tokens
|
||||
2. Click "Generate new token (classic)"
|
||||
3. Give it a name: "Issue Import Script"
|
||||
4. Select scope: **`repo`** (full control of private repositories)
|
||||
5. Click "Generate token"
|
||||
6. Copy the token (you won't see it again!)
|
||||
|
||||
### 3. Set Token
|
||||
|
||||
```bash
|
||||
export GITHUB_TOKEN=your_github_token_here
|
||||
```
|
||||
|
||||
Or add to your shell profile (~/.bashrc, ~/.zshrc):
|
||||
```bash
|
||||
echo 'export GITHUB_TOKEN=your_token_here' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
### 4. Run the Script
|
||||
|
||||
```bash
|
||||
node scripts/import-github-issues.js
|
||||
```
|
||||
|
||||
## What It Does
|
||||
|
||||
The script will:
|
||||
1. ✅ Create all necessary labels (P0, P1, P2, bug, feature, etc.)
|
||||
2. 📄 Parse `docs/issues/P0-ISSUES.md`, `P1-ISSUES.md`, `P2-ISSUES.md`
|
||||
3. 🎯 Create GitHub Issues with:
|
||||
- Proper titles with priority prefix
|
||||
- Full issue body with formatting
|
||||
- Appropriate labels
|
||||
- Links to referenced files
|
||||
|
||||
4. 📊 Output summary with issue URLs
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
🚀 GitHub Issues Import Script
|
||||
|
||||
📝 Ensuring labels exist...
|
||||
✓ Label "P0" exists
|
||||
✓ Created label "P1"
|
||||
✓ Created label "bug"
|
||||
|
||||
📄 Processing P0-ISSUES.md...
|
||||
Found 5 issues
|
||||
|
||||
✓ Created: [P0] Fix onboarding progress loss on page refresh
|
||||
→ https://github.com/AeThex-Corporation/aethex-forge/issues/1
|
||||
✓ Created: [P0] Complete Stripe payment integration
|
||||
→ https://github.com/AeThex-Corporation/aethex-forge/issues/2
|
||||
...
|
||||
|
||||
✅ Done! Created 25 GitHub issues
|
||||
|
||||
View issues: https://github.com/AeThex-Corporation/aethex-forge/issues
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error: GITHUB_TOKEN not set
|
||||
```bash
|
||||
export GITHUB_TOKEN=your_token_here
|
||||
```
|
||||
|
||||
### Error: 404 Not Found
|
||||
- Check that REPO_OWNER and REPO_NAME in the script match your repository
|
||||
- Verify your token has `repo` scope
|
||||
|
||||
### Error: 403 Forbidden
|
||||
- Your token doesn't have permission
|
||||
- Generate a new token with `repo` scope
|
||||
|
||||
### Error: npm ERR! Cannot find module '@octokit/rest'
|
||||
```bash
|
||||
npm install @octokit/rest
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
Edit `scripts/import-github-issues.js`:
|
||||
|
||||
```javascript
|
||||
// Change repository
|
||||
const REPO_OWNER = 'YourGitHubUsername';
|
||||
const REPO_NAME = 'your-repo-name';
|
||||
|
||||
// Add custom labels
|
||||
const LABELS = {
|
||||
P0: { name: 'P0', color: 'B60205', description: 'Critical' },
|
||||
// Add more...
|
||||
};
|
||||
```
|
||||
|
||||
## What Gets Created
|
||||
|
||||
### Labels
|
||||
- **P0** (red) - Critical priority
|
||||
- **P1** (orange) - Medium priority
|
||||
- **P2** (yellow) - Low priority
|
||||
- **bug** (red) - Something isn't working
|
||||
- **feature** (green) - New feature
|
||||
- **enhancement** (blue) - Enhancement
|
||||
- **tech-debt** (purple) - Technical debt
|
||||
- **security** (red) - Security issue
|
||||
|
||||
### Issues from P0-ISSUES.md
|
||||
1. Fix onboarding progress loss (bug, P0)
|
||||
2. Complete Stripe integration (bug, P0)
|
||||
3. Refactor large components (tech-debt, P0)
|
||||
4. Add error tracking (feature, P0)
|
||||
5. Add form validation (feature, P0)
|
||||
|
||||
### Issues from P1-ISSUES.md
|
||||
1. Build notification system (feature, P1)
|
||||
2. Complete project workflows (feature, P1)
|
||||
3. Add image upload (feature, P1)
|
||||
4. Implement moderation (feature, P1)
|
||||
5. Add 2FA (security, P1)
|
||||
|
||||
### Issues from P2-ISSUES.md
|
||||
15 enhancement issues (dark mode, i18n, PWA, etc.)
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Dry Run (Preview)
|
||||
Edit the script and comment out the `createIssue` call to see what would be created:
|
||||
|
||||
```javascript
|
||||
// await createIssue(issue); // Comment this out
|
||||
console.log('Would create:', issue.title);
|
||||
```
|
||||
|
||||
### Assign Issues
|
||||
Add assignees to created issues:
|
||||
|
||||
```javascript
|
||||
await octokit.rest.issues.create({
|
||||
// ...existing params
|
||||
assignees: ['your-github-username'],
|
||||
});
|
||||
```
|
||||
|
||||
### Create Milestone
|
||||
Group issues under a milestone:
|
||||
|
||||
```javascript
|
||||
// Create milestone first
|
||||
const milestone = await octokit.rest.issues.createMilestone({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
title: 'Q1 2026 Priorities',
|
||||
});
|
||||
|
||||
// Then assign to issues
|
||||
await octokit.rest.issues.create({
|
||||
// ...existing params
|
||||
milestone: milestone.data.number,
|
||||
});
|
||||
```
|
||||
|
||||
## Clean Up
|
||||
|
||||
To delete all created issues (use with caution!):
|
||||
|
||||
```bash
|
||||
# List all issues
|
||||
gh issue list --limit 100
|
||||
|
||||
# Close specific issue
|
||||
gh issue close <issue-number>
|
||||
|
||||
# Or delete (requires admin access)
|
||||
gh api -X DELETE /repos/OWNER/REPO/issues/ISSUE_NUMBER
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Script waits 1 second between creating issues to respect GitHub API rate limits
|
||||
- Issues are created in order: P0 → P1 → P2
|
||||
- Existing labels won't be overwritten
|
||||
- Run as many times as needed (won't create duplicates if you don't re-run)
|
||||
210
scripts/import-github-issues.js
Executable file
210
scripts/import-github-issues.js
Executable file
|
|
@ -0,0 +1,210 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* GitHub Issues Import Script
|
||||
*
|
||||
* Converts markdown issue documentation into GitHub Issues
|
||||
* Reads from docs/issues/ and creates issues with proper labels
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/import-github-issues.js
|
||||
*
|
||||
* Requirements:
|
||||
* - GitHub Personal Access Token with repo scope
|
||||
* - Set GITHUB_TOKEN environment variable
|
||||
* - npm install @octokit/rest
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
// Configuration
|
||||
const REPO_OWNER = 'AeThex-Corporation';
|
||||
const REPO_NAME = 'aethex-forge';
|
||||
const ISSUES_DIR = path.join(__dirname, '../docs/issues');
|
||||
|
||||
// Initialize Octokit
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN,
|
||||
});
|
||||
|
||||
// Label definitions
|
||||
const LABELS = {
|
||||
P0: { name: 'P0', color: 'B60205', description: 'Critical priority - fix ASAP' },
|
||||
P1: { name: 'P1', color: 'D93F0B', description: 'Medium priority' },
|
||||
P2: { name: 'P2', color: 'FBCA04', description: 'Low priority / nice to have' },
|
||||
bug: { name: 'bug', color: 'D73A4A', description: 'Something isn\'t working' },
|
||||
feature: { name: 'feature', color: '0E8A16', description: 'New feature or request' },
|
||||
enhancement: { name: 'enhancement', color: 'A2EEEF', description: 'Enhancement to existing feature' },
|
||||
'tech-debt': { name: 'tech-debt', color: 'D876E3', description: 'Technical debt' },
|
||||
security: { name: 'security', color: 'B60205', description: 'Security issue' },
|
||||
documentation: { name: 'documentation', color: '0075CA', description: 'Documentation improvements' },
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse markdown file into individual issues
|
||||
*/
|
||||
function parseMarkdownIssues(content, priority) {
|
||||
const issues = [];
|
||||
|
||||
// Split by "## Issue" headings
|
||||
const sections = content.split(/^## Issue \d+:/m);
|
||||
|
||||
// Skip the first section (it's the header before first issue)
|
||||
for (let i = 1; i < sections.length; i++) {
|
||||
const section = sections[i].trim();
|
||||
|
||||
// Extract title (first line with brackets removed)
|
||||
const titleMatch = section.match(/^\[.*?\]\s*(.+?)$/m);
|
||||
if (!titleMatch) continue;
|
||||
|
||||
const title = titleMatch[1].trim();
|
||||
|
||||
// Extract labels from title
|
||||
const labels = [priority];
|
||||
if (title.toLowerCase().includes('fix') || section.includes('**Labels:** `bug`')) {
|
||||
labels.push('bug');
|
||||
} else if (section.includes('**Labels:** `feature`')) {
|
||||
labels.push('feature');
|
||||
} else if (section.includes('**Labels:** `enhancement`')) {
|
||||
labels.push('enhancement');
|
||||
} else if (section.includes('**Labels:** `tech-debt`')) {
|
||||
labels.push('tech-debt');
|
||||
} else if (section.includes('**Labels:** `security`')) {
|
||||
labels.push('security');
|
||||
}
|
||||
|
||||
// Get the body (everything after the title line)
|
||||
const bodyStartIndex = section.indexOf('\n');
|
||||
const body = section.substring(bodyStartIndex).trim();
|
||||
|
||||
issues.push({
|
||||
title: `[${priority}] ${title}`,
|
||||
body,
|
||||
labels,
|
||||
});
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure all labels exist in the repository
|
||||
*/
|
||||
async function ensureLabels() {
|
||||
console.log('📝 Ensuring labels exist...');
|
||||
|
||||
for (const [key, label] of Object.entries(LABELS)) {
|
||||
try {
|
||||
await octokit.rest.issues.getLabel({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
name: label.name,
|
||||
});
|
||||
console.log(` ✓ Label "${label.name}" exists`);
|
||||
} catch (error) {
|
||||
if (error.status === 404) {
|
||||
// Label doesn't exist, create it
|
||||
await octokit.rest.issues.createLabel({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
name: label.name,
|
||||
color: label.color,
|
||||
description: label.description,
|
||||
});
|
||||
console.log(` ✓ Created label "${label.name}"`);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single GitHub issue
|
||||
*/
|
||||
async function createIssue(issue) {
|
||||
try {
|
||||
const response = await octokit.rest.issues.create({
|
||||
owner: REPO_OWNER,
|
||||
repo: REPO_NAME,
|
||||
title: issue.title,
|
||||
body: issue.body,
|
||||
labels: issue.labels,
|
||||
});
|
||||
|
||||
console.log(` ✓ Created: ${issue.title}`);
|
||||
console.log(` → ${response.data.html_url}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(` ✗ Failed to create: ${issue.title}`);
|
||||
console.error(` Error: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main execution
|
||||
*/
|
||||
async function main() {
|
||||
console.log('🚀 GitHub Issues Import Script\n');
|
||||
|
||||
// Check for GitHub token
|
||||
if (!process.env.GITHUB_TOKEN) {
|
||||
console.error('❌ Error: GITHUB_TOKEN environment variable not set');
|
||||
console.error('\nTo fix this:');
|
||||
console.error('1. Go to https://github.com/settings/tokens');
|
||||
console.error('2. Create a Personal Access Token with "repo" scope');
|
||||
console.error('3. Run: export GITHUB_TOKEN=your_token_here');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Ensure labels exist
|
||||
await ensureLabels();
|
||||
console.log('');
|
||||
|
||||
// Process each priority file
|
||||
const files = [
|
||||
{ file: 'P0-ISSUES.md', priority: 'P0' },
|
||||
{ file: 'P1-ISSUES.md', priority: 'P1' },
|
||||
{ file: 'P2-ISSUES.md', priority: 'P2' },
|
||||
];
|
||||
|
||||
let totalCreated = 0;
|
||||
|
||||
for (const { file, priority } of files) {
|
||||
const filePath = path.join(ISSUES_DIR, file);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.warn(`⚠️ File not found: ${file}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`📄 Processing ${file}...`);
|
||||
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const issues = parseMarkdownIssues(content, priority);
|
||||
|
||||
console.log(` Found ${issues.length} issues\n`);
|
||||
|
||||
for (const issue of issues) {
|
||||
await createIssue(issue);
|
||||
totalCreated++;
|
||||
|
||||
// Rate limiting: wait 1 second between requests
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
console.log('');
|
||||
}
|
||||
|
||||
console.log(`✅ Done! Created ${totalCreated} GitHub issues`);
|
||||
console.log(`\nView issues: https://github.com/${REPO_OWNER}/${REPO_NAME}/issues`);
|
||||
}
|
||||
|
||||
// Run the script
|
||||
main().catch((error) => {
|
||||
console.error('❌ Script failed:', error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Reference in a new issue