checkpoint(cover): backend ready before AI development
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
# API key for chatgpt
|
||||
CHATGPT_KEY=api key for chatgpt
|
||||
# This is secret key for jwt signature
|
||||
JWT_SECRET=just a random string here
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
||||
data
|
||||
.DS_Store
|
||||
.env
|
||||
|
||||
@@ -19,4 +19,5 @@ func LoadEnv() {
|
||||
Env["db"] = defaultValue(os.Getenv("POSTGRES_DB"), "postgresql://postgres:postgres@db:5432/cover-letter")
|
||||
Env["JWT_SECRET"] = defaultValue(os.Getenv("JWT_SECRET"), "just a random string here")
|
||||
Env["Environment"] = defaultValue(os.Getenv("Environment"), "dev")
|
||||
Env["CHATGPT_KEY"] = defaultValue(os.Getenv("CHATGPT_KEY"), "")
|
||||
}
|
||||
|
||||
55
backend/controllers/cover/cover.go
Normal file
55
backend/controllers/cover/cover.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package cover
|
||||
|
||||
import (
|
||||
"backend/models/template"
|
||||
"backend/utils"
|
||||
"backend/utils/jwt"
|
||||
res "backend/utils/responses"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
var validate = validator.New()
|
||||
|
||||
func Get(c *gin.Context) {
|
||||
}
|
||||
|
||||
type CoverPost struct {
|
||||
TemplateId string `json:"templateId" validate:"required,number,min=1"`
|
||||
Application string `json:"application" validate:"required,min=50"`
|
||||
}
|
||||
|
||||
func Post(c *gin.Context) {
|
||||
// Receive data from frontend, check if data is okay, hash password, call model
|
||||
var data CoverPost
|
||||
if err := utils.BindAndValidate(&data, c); err != nil {
|
||||
res.Error(c, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Get user data from the token
|
||||
user, err := jwt.GetUser(c)
|
||||
if err != nil {
|
||||
res.NeedsToLogin(c)
|
||||
return
|
||||
}
|
||||
|
||||
// Get tempalte
|
||||
templates, err := template.Get("user_id = $1 AND id = $2", user.Id, data.TemplateId)
|
||||
if err != nil {
|
||||
res.Error(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Call chat and ask for cover letter nicely
|
||||
|
||||
res.Success(c, templates)
|
||||
}
|
||||
|
||||
func Put(c *gin.Context) {
|
||||
}
|
||||
|
||||
func Delete(c *gin.Context) {
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"backend/controllers/cover"
|
||||
"backend/controllers/template"
|
||||
"backend/controllers/user"
|
||||
"backend/middleware"
|
||||
@@ -29,5 +30,9 @@ func SetupRoutes() *gin.Engine {
|
||||
// PUT (Edit)
|
||||
// DELETE (Delete)
|
||||
|
||||
// Cover letter routes
|
||||
covers := auth.Group("/cover")
|
||||
covers.POST("", cover.Post)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
1
backend/utils/chatgpt/chatgpt.go
Normal file
1
backend/utils/chatgpt/chatgpt.go
Normal file
@@ -0,0 +1 @@
|
||||
package chatgpt
|
||||
29
backend/utils/checkData.go
Normal file
29
backend/utils/checkData.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
var validate = validator.New()
|
||||
|
||||
func BindAndValidate(data any, c *gin.Context) error {
|
||||
fmt.Println("🔍 BindAndValidate called")
|
||||
|
||||
if err := c.ShouldBindJSON(data); err != nil {
|
||||
fmt.Println("❌ Bind error:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("✅ Bind success:", data)
|
||||
|
||||
if err := validate.Struct(data); err != nil {
|
||||
fmt.Println("❌ Validation error:", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("✅ Validation success")
|
||||
return nil
|
||||
}
|
||||
@@ -10,7 +10,7 @@ services:
|
||||
volumes:
|
||||
- "./backend:/app"
|
||||
env_file:
|
||||
- ./backend/.env
|
||||
- .env
|
||||
environment:
|
||||
# - GIN_MODE=release # For production
|
||||
- GIN_MODE=debug
|
||||
|
||||
@@ -28,8 +28,10 @@ export default function SelectField({ data, label, className = "" }: SelectProps
|
||||
)}
|
||||
|
||||
<Select
|
||||
onValueChange={field.handleChange}
|
||||
onOpenChange={field.handleBlur}
|
||||
onValueChange={(val) => {
|
||||
field.handleChange(val);
|
||||
field.handleBlur();
|
||||
}}
|
||||
defaultValue={field.state.value}
|
||||
>
|
||||
<SelectTrigger className={"w-full"}>
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import renderQueryState from "@/components/RenderQueryState";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useAppForm } from "@/hooks/formHook";
|
||||
import Authorised from "@/layouts/Authorised";
|
||||
import requests from "@/lib/requests";
|
||||
import type { Template } from "@/types/api";
|
||||
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const Route = createFileRoute("/cover/create")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
const createSchema = z.object({
|
||||
templateId: z.string().min(1, "Please select template"),
|
||||
application: z.string().min(50, "Application is too short"),
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const templates = useQuery({
|
||||
queryKey: ["user_templates"],
|
||||
@@ -28,6 +33,12 @@ function RouteComponent() {
|
||||
templateId: "",
|
||||
application: "Paste job application here",
|
||||
},
|
||||
validators: {
|
||||
onBlur: createSchema,
|
||||
},
|
||||
onSubmit({ value }) {
|
||||
console.log(JSON.stringify(value));
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -41,7 +52,7 @@ function RouteComponent() {
|
||||
name="templateId"
|
||||
children={(f) => (
|
||||
<f.SelectField
|
||||
data={templates.data?.map((t) => ({ value: t.id, name: t.name }))}
|
||||
data={templates.data?.map((t) => ({ value: `${t.id}`, name: t.name }))}
|
||||
label={"Select template for cover letter"}
|
||||
/>
|
||||
)}
|
||||
@@ -53,7 +64,9 @@ function RouteComponent() {
|
||||
<form.AppField name="application" children={(f) => <f.RichTextEdit />} />
|
||||
</div>
|
||||
|
||||
<Button className="mt-4">Generate cover letter</Button>
|
||||
<Button onClick={form.handleSubmit} className="mt-4">
|
||||
Generate cover letter
|
||||
</Button>
|
||||
</Authorised>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user