Merge branch 'main' into phase_3_1

This commit is contained in:
Zvonimir Sabljic
2025-01-08 10:13:03 +01:00
21 changed files with 63 additions and 22 deletions

View File

@@ -8,7 +8,6 @@ from core.config import FRONTEND_AGENT_NAME
from core.llm.parser import DescriptiveCodeBlockParser
from core.log import get_logger
from core.telemetry import telemetry
from core.templates.base import NoOptions
from core.templates.registry import PROJECT_TEMPLATES
log = get_logger(__name__)
@@ -44,6 +43,20 @@ class Frontend(FileDiffMixin, BaseAgent):
)
description = description.text.strip()
auth_needed = await self.ask_question(
"Do you need authentication in your app (login, register, etc.)?",
buttons={
"yes": "Yes",
"no": "No",
},
buttons_only=True,
default="no",
)
options = {
"auth": auth_needed.button == "yes",
}
self.state_manager.user_options = options
await self.send_message("Setting up the project...")
self.next_state.epics = [
@@ -58,7 +71,7 @@ class Frontend(FileDiffMixin, BaseAgent):
}
]
await self.apply_template()
await self.apply_template(options)
return False
@@ -240,7 +253,7 @@ class Frontend(FileDiffMixin, BaseAgent):
return AgentResponse.done(self)
async def apply_template(self):
async def apply_template(self, options: dict = {}):
"""
Applies a template to the frontend.
"""
@@ -251,7 +264,7 @@ class Frontend(FileDiffMixin, BaseAgent):
return
template = template_class(
NoOptions(),
options,
self.state_manager,
self.process_manager,
)

View File

@@ -48,8 +48,8 @@ You are currently working on task #{{ current_task_index + 1 }} with the followi
```
In this task, you need to focus on implementing the following endpoints:
{% for endpoint in related_api_endpoints %}
{{ "`" ~ endpoint ~ "`" }}{% if not loop.last %}
{% for api in related_api_endpoints %}
{{ "`" ~ api.endpoint ~ "`" }}{% if not loop.last %}
{% endif %}
{% endfor %}
You must implement the backend API endpoints, remove the mocked that on the frontend side, and replace it with the real API request, implement the database model (if it's not implemented already), and implement the utility function (eg. 3rd party integration) that is needed for this endpoint. Whenever you need to create a utility function that uses a 3rd party integration, you **MUST** mock that function (create the function, return a mocked data, and specify the structure of the input and the output of the function in the comment above the function).

View File

@@ -13,7 +13,7 @@ When thinking about the scope of a single task, here are the components that nee
**IMPORTANT: order of tasks**
The tasks you create **MUST** be in the order that they should be implemented. When CRUD operations need to be implemented, first implement the Create operation, then Read, Update, and Delete.
{% if state.has_frontend() and not state.is_feature() %}
{% if state.has_frontend() and not state.is_feature() and state.user_options.auth %}
**IMPORTANT**
If you are working on the Epic #1 that needs to implement the authentication system. The first task **MUST** be to remove the mocked data for authentication (register and login). After that, add any custom authentication requirements like different roles, different user data, etc.
{% endif %}

View File

@@ -25,7 +25,7 @@ Before we go into the coding part, your job is to split the development process
Now, based on the project details provided{% if task_type == 'feature' %} and new feature description{% endif %}, think epic by epic and create the entire development plan{% if task_type == 'feature' %} for new feature{% elif task_type == 'app' %}. {% if state.files %}Continue from the existing code listed above{% else %}Start from the project setup{% endif %} and specify each epic until the moment when the entire app should be fully working{% if state.files %}. IMPORTANT: You should not reimplement what's already done - just continue from the implementation already there.{% endif %}{% endif %}
IMPORTANT!
Frontend is already built and you don't need to create epics for it. You only need to create epics for backend implementation and connect it to existing frontend. Keep in mind that some backend functionality is already implemented.{% if task_type == 'app' %} The first epic **MUST** be to implement the authentication system if it's required.{% endif %}
Frontend is already built and you don't need to create epics for it. You only need to create epics for backend implementation and connect it to existing frontend. Keep in mind that some backend functionality is already implemented.{% if task_type == 'app' and state.user_options.auth %} The first epic **MUST** be to implement the authentication system if it's required.{% endif %}
Strictly follow these rules:

View File

@@ -52,6 +52,7 @@ class StateManager:
self.blockDb = False
self.git_available = False
self.git_used = False
self.user_options = {}
@asynccontextmanager
async def db_blocker(self):

View File

@@ -8,21 +8,21 @@ This app has 2 parts:
* It is running on port 5173 and this port should be used for user testing when possible
* All requests to the backend need to go to an endpoint that starts with `/api/` (e.g. `/api/companies`)
* Implememented pages:
* Home - home (index) page (`/`)
* Home - home (index) page (`/`){% if options.auth %}
* Login - login page (`/login/`) - on login, stores the auth tokens to `accessToken` and `refreshToken` variables in local storage
* Register - register page (`/register/`) - on register, store **ONLY** the `accessToken` variable in local storage
* Register - register page (`/register/`) - on register, store **ONLY** the `accessToken` variable in local storage{% endif %}
** #2 Backend **
* Express-based server implementing REST API endpoints in `api/`
* Has codebase inside "server/" folder
* Backend is running on port 3000
* MongoDB database support with Mongoose
* MongoDB database support with Mongoose{% if options.auth %}
* Token-based authentication (using bearer access and refresh tokens)
* User authentication (email + password):
* login/register API endpoints in `/server/routes/auth.js`
* authorization middleware in `/server/routes/middleware/auth.js`
* user management logic in `/server/routes/services/user.js`
* User authentication is implemented and doesn't require any additional work
* User authentication is implemented and doesn't require any additional work{% endif %}
Concurrently is used to run both client and server together with a single command (`npm run start`).

View File

@@ -129,6 +129,7 @@ class Renderer:
continue
contents = self.render_template(tpl_location, context)
retval[output_location] = contents
if contents != "":
retval[output_location] = contents
return retval

View File

@@ -12,8 +12,10 @@ function App() {
<ThemeProvider defaultTheme="light" storageKey="ui-theme">
<Router>
<Routes>
{% if options.auth %}
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
{% endif %}
<Route path="/" element={<ProtectedRoute> <Layout /> </ProtectedRoute>} />
</Routes>
</Router>

View File

@@ -12,7 +12,7 @@ const api = axios.create({
});
let accessToken: string | null = null;
{% if options.auth %}
// Axios request interceptor: Attach access token to headers
api.interceptors.request.use(
(config: AxiosRequestConfig): AxiosRequestConfig => {
@@ -62,6 +62,6 @@ api.interceptors.response.use(
return Promise.reject(error); // Pass other errors through
}
);
{% endif %}
export default api;

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
import api from './api';
// Description: Login user functionality
@@ -40,3 +41,4 @@ export const logout = async () => {
throw new Error(error?.response?.data?.message || error.message);
}
};
{% endif %}

View File

@@ -1,27 +1,27 @@
{% raw %}
import { Bell, LogOut } from "lucide-react"
import { Bell{% if options.auth %}, LogOut{% endif %} } from "lucide-react"
import { Button } from "./ui/button"
import { ThemeToggle } from "./ui/theme-toggle"
import { useAuth } from "@/contexts/AuthContext"
import { useNavigate } from "react-router-dom"
export function Header() {
const { logout } = useAuth()
export function Header() {{% if options.auth %}
const { logout } = useAuth(){% endif %}
const navigate = useNavigate()
{% if options.auth %}
const handleLogout = () => {
logout()
navigate("/login")
}
}{% endif %}
return (
<header className="fixed top-0 z-50 w-full border-b bg-background/80 backdrop-blur-sm">
<div className="flex h-16 items-center justify-between px-6">
<div className="text-xl font-bold">Home</div>
<div className="flex items-center gap-4">
<ThemeToggle />
<ThemeToggle />{% if options.auth %}
<Button variant="ghost" size="icon" onClick={handleLogout}>
<LogOut className="h-5 w-5" />
</Button>
</Button>{% endif %}
</div>
</div>
</header>

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
{% raw %}
import { createContext, useContext, useState, ReactNode } from "react";
import { login as apiLogin, register as apiRegister } from "@/api/auth";
@@ -66,3 +67,4 @@ export function useAuth() {
return context;
}
{% endraw %}
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
import { useState } from "react"
import { useForm } from "react-hook-form"
import { useNavigate } from "react-router-dom"
@@ -100,3 +101,4 @@ export function Login() {
</div>
)
}
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
import { useState } from "react"
import { useForm } from "react-hook-form"
import { useNavigate } from "react-router-dom"
@@ -101,3 +102,4 @@ export function Register() {
</div>
)
}
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
const mongoose = require('mongoose');
const { validatePassword, isPasswordHash } = require('../utils/password.js');
@@ -52,3 +53,4 @@ schema.set('toJSON', {
const User = mongoose.model('User', schema);
module.exports = User;
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
const express = require('express');
const UserService = require('../services/userService.js');
const { requireUser } = require('./middleware/auth.js');
@@ -124,3 +125,4 @@ router.get('/me', requireUser, async (req, res) => {
});
module.exports = router;
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
const UserService = require('../../services/userService.js');
const jwt = require('jsonwebtoken');
@@ -17,3 +18,4 @@ const requireUser = (req, res, next) => {
module.exports = {
requireUser,
};
{% endif %}

View File

@@ -5,7 +5,9 @@ const express = require("express");
const session = require("express-session");
const MongoStore = require('connect-mongo');
const basicRoutes = require("./routes/index");
{% if options.auth %}
const authRoutes = require("./routes/authRoutes");
{% endif %}
const { connectDB } = require("./config/database");
const cors = require("cors");
@@ -38,8 +40,10 @@ app.on("error", (error) => {
// Basic Routes
app.use(basicRoutes);
{% if options.auth %}
// Authentication Routes
app.use('/api/auth', authRoutes);
{% endif %}
// If no routes handled the request, it's a 404
app.use((req, res, next) => {

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
const { randomUUID } = require('crypto');
const User = require('../models/User.js');
@@ -104,3 +105,4 @@ class UserService {
}
module.exports = UserService;
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
const jwt = require('jsonwebtoken');
const generateAccessToken = (user) => {
@@ -12,3 +13,4 @@ module.exports = {
generateAccessToken,
generateRefreshToken
};
{% endif %}

View File

@@ -1,3 +1,4 @@
{% if options.auth %}
const bcrypt = require('bcrypt');
/**
@@ -42,3 +43,4 @@ module.exports = {
validatePassword,
isPasswordHash,
}
{% endif %}