Started BE structure / docker for FE dev

This commit is contained in:
Leons Aleksandrovs
2025-07-03 17:55:15 +03:00
parent df38c61069
commit 6130fc8abf
15 changed files with 278 additions and 19 deletions

View File

@@ -1,11 +1,10 @@
FROM golang:1.24.4-alpine3.22@sha256:68932fa6d4d4059845c8f40ad7e654e626f3ebd3706eef7846f319293ab5cb7a as base
FROM golang:1.24.4-alpine3.22@sha256:68932fa6d4d4059845c8f40ad7e654e626f3ebd3706eef7846f319293ab5cb7a AS base
# Set up workdir
WORKDIR /app
# Install dependencies
# COPY go.mod go.sum ./
COPY go.mod ./
COPY go.mod go.sum ./
RUN go mod download
# Copy code, and compile

View File

@@ -0,0 +1,14 @@
# ------ Dependencies stage ------
FROM oven/bun:1.2.18-alpine@sha256:a7df687a2f684ee2f7404e2592039e192d75d26a04f843e60d9fc342741187d0 AS deps
WORKDIR /app
# Install dependencies
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
# ------ Development stage ------
FROM deps AS dev
# Run
CMD ["bun", "run", "dev"]

55
README.md Normal file
View File

@@ -0,0 +1,55 @@
# Cover letter templater
Generates cover letters based on a template, and job listening using OpenAI.
## Stack
- Frontend:
- `Reactjs`
- `Tailwindcss`
- `Mantine` component library
- TanStack `Router` + `Query` + `Forms`
- Backend: `Golang`
- Database: `PostgreSQL`
- Deployment: `Docker`
## Backend
### Structure example (TODO: Replace with actual one)
```sh
backend/
├── main.go # Entry point
├── api/ # Route handlers grouped by domain
│ ├── auth.go
│ └── coverletters.go
├── routes/ # HTTP route registration
│ └── routes.go
├── middleware/ # Custom middleware (logging, auth)
│ └── auth.go
├── models/ # Data models, structs, db access
│ ├── user.go
│ └── coverletter.go
├── services/ # Business logic (e.g. OpenAI integration)
│ ├── auth_service.go
│ └── coverletter_service.go
├── utils/ # Shared helpers/utilities
│ └── jwt.go
└── config/ # Env loading, settings
└── config.go
```
## Frontend
### Structure example
TODO: ADD STRUCTURE EXAMPLE
## Deployement
TODO: ADD DEPLOYMENT INSTRUCTIONS
## Development
TODO: ADD DEV INSTRUCTIONS

20
backend/config/config.go Normal file
View File

@@ -0,0 +1,20 @@
package config
import "os"
func defaultValue(val string, def string) string {
if val == "" {
return def
}
return val
}
func LoadEnv() map[string]string {
// Create object where to store used variables
env := make(map[string]string, 1)
// Get env variables that will be used while server is running
env["port"] = defaultValue(os.Getenv("PORT"), "8080")
return env
}

View File

@@ -1,3 +1,5 @@
module backend
go 1.24.4
require github.com/go-chi/chi/v5 v5.2.2

2
backend/go.sum Normal file
View File

@@ -0,0 +1,2 @@
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=

View File

@@ -1,11 +1,20 @@
package main
import "time"
import (
"backend/config"
"backend/routes"
"log"
"net/http"
)
func main() {
println("Hello, world!")
// Load env variables
env := config.LoadEnv()
for {
time.Sleep(1 * time.Second)
}
// Setup routes
routes := routes.SetupRoutes()
// Listen on port smth
log.Printf("Starting server on %s PORT\n", env["port"])
log.Fatal(http.ListenAndServe(":"+env["port"], routes))
}

19
backend/routes/routes.go Normal file
View File

@@ -0,0 +1,19 @@
package routes
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func SetupRoutes() http.Handler {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
})
return r
}

View File

@@ -5,7 +5,23 @@ services:
dockerfile: ../Dockerfile.backend
container_name: cover-letter-backend
environment:
- PORT=8080 # Internal container port
networks:
- cover-letter-network
frontend:
build:
context: ./frontend
dockerfile: ../Dockerfile.frontend
target: dev # Development stage
container_name: cover-letter-frontend
volumes:
- "./frontend:/app" # Mount frontend
- "/app/node_modules" # Ignore node_modules
ports:
- 3000:3000
networks:
- cover-letter-network

1
frontend/.dockerignore Normal file
View File

@@ -0,0 +1 @@
node_modules/*

View File

@@ -1,10 +1,9 @@
{
"name": ".",
"name": "Cover letter frontend",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"start": "vite --port 3000",
"dev": "vite --port 3000 --host",
"build": "vite build && tsc",
"serve": "vite preview",
"test": "vitest run"
@@ -34,4 +33,5 @@
"vitest": "^3.0.5",
"web-vitals": "^4.2.4"
}
}
}

View File

@@ -0,0 +1,122 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as DemoTanstackQueryRouteImport } from './routes/demo.tanstack-query'
import { Route as DemoFormSimpleRouteImport } from './routes/demo.form.simple'
import { Route as DemoFormAddressRouteImport } from './routes/demo.form.address'
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({
id: '/demo/tanstack-query',
path: '/demo/tanstack-query',
getParentRoute: () => rootRouteImport,
} as any)
const DemoFormSimpleRoute = DemoFormSimpleRouteImport.update({
id: '/demo/form/simple',
path: '/demo/form/simple',
getParentRoute: () => rootRouteImport,
} as any)
const DemoFormAddressRoute = DemoFormAddressRouteImport.update({
id: '/demo/form/address',
path: '/demo/form/address',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
'/demo/form/address': typeof DemoFormAddressRoute
'/demo/form/simple': typeof DemoFormSimpleRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
'/demo/form/address': typeof DemoFormAddressRoute
'/demo/form/simple': typeof DemoFormSimpleRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
'/demo/form/address': typeof DemoFormAddressRoute
'/demo/form/simple': typeof DemoFormSimpleRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths:
| '/'
| '/demo/tanstack-query'
| '/demo/form/address'
| '/demo/form/simple'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/demo/tanstack-query' | '/demo/form/address' | '/demo/form/simple'
id:
| '__root__'
| '/'
| '/demo/tanstack-query'
| '/demo/form/address'
| '/demo/form/simple'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
DemoFormAddressRoute: typeof DemoFormAddressRoute
DemoFormSimpleRoute: typeof DemoFormSimpleRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/demo/tanstack-query': {
id: '/demo/tanstack-query'
path: '/demo/tanstack-query'
fullPath: '/demo/tanstack-query'
preLoaderRoute: typeof DemoTanstackQueryRouteImport
parentRoute: typeof rootRouteImport
}
'/demo/form/simple': {
id: '/demo/form/simple'
path: '/demo/form/simple'
fullPath: '/demo/form/simple'
preLoaderRoute: typeof DemoFormSimpleRouteImport
parentRoute: typeof rootRouteImport
}
'/demo/form/address': {
id: '/demo/form/address'
path: '/demo/form/address'
fullPath: '/demo/form/address'
preLoaderRoute: typeof DemoFormAddressRouteImport
parentRoute: typeof rootRouteImport
}
}
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
DemoFormAddressRoute: DemoFormAddressRoute,
DemoFormSimpleRoute: DemoFormSimpleRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()

View File

@@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router'
import { useAppForm } from '../hooks/demo.form'
export const Route = createFileRoute('/demo/form')({
export const Route = createFileRoute('/demo/form/address')({
component: AddressForm,
})

View File

@@ -3,7 +3,7 @@ import { z } from 'zod'
import { useAppForm } from '../hooks/demo.form'
export const Route = createFileRoute('/demo/form')({
export const Route = createFileRoute('/demo/form/simple')({
component: SimpleForm,
})

View File

@@ -1,9 +1,9 @@
import { createFileRoute } from '@tanstack/react-router'
import logo from '../logo.svg'
import { createFileRoute } from "@tanstack/react-router";
import logo from "../logo.svg";
export const Route = createFileRoute('/')({
export const Route = createFileRoute("/")({
component: App,
})
});
function App() {
return (
@@ -35,5 +35,5 @@ function App() {
</a>
</header>
</div>
)
);
}