update
This commit is contained in:
5
.env.docker
Normal file
5
.env.docker
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
APP_URL=
|
||||||
|
OIDC_ISSUER_URL=
|
||||||
|
OIDC_CLIENT_ID=
|
||||||
|
SESSION_SECRET=
|
||||||
|
API_URL=
|
||||||
17
.env.example
17
.env.example
@@ -1,17 +0,0 @@
|
|||||||
# Database
|
|
||||||
DATABASE_URL="postgresql://user:password@localhost:5432/timetracker"
|
|
||||||
|
|
||||||
# OIDC Configuration
|
|
||||||
OIDC_ISSUER_URL="https://your-oidc-provider.com"
|
|
||||||
OIDC_CLIENT_ID="your-client-id"
|
|
||||||
OIDC_REDIRECT_URI="http://localhost:3000/auth/callback"
|
|
||||||
|
|
||||||
# Session
|
|
||||||
SESSION_SECRET="your-session-secret-min-32-characters"
|
|
||||||
|
|
||||||
# Server
|
|
||||||
PORT=3001
|
|
||||||
NODE_ENV=development
|
|
||||||
|
|
||||||
# Frontend URL (for CORS)
|
|
||||||
FRONTEND_URL="http://localhost:5173"
|
|
||||||
@@ -86,36 +86,41 @@ SESSION_SECRET="your-secure-session-secret-min-32-chars"
|
|||||||
# Server
|
# Server
|
||||||
PORT=3001
|
PORT=3001
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
FRONTEND_URL="http://localhost:5173"
|
APP_URL="http://localhost:5173"
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
- `GET /auth/login` - Initiate OIDC login
|
- `GET /auth/login` - Initiate OIDC login
|
||||||
- `GET /auth/callback` - OIDC callback
|
- `GET /auth/callback` - OIDC callback
|
||||||
- `POST /auth/logout` - End session
|
- `POST /auth/logout` - End session
|
||||||
- `GET /auth/me` - Get current user
|
- `GET /auth/me` - Get current user
|
||||||
|
|
||||||
### Clients
|
### Clients
|
||||||
|
|
||||||
- `GET /api/clients` - List clients
|
- `GET /api/clients` - List clients
|
||||||
- `POST /api/clients` - Create client
|
- `POST /api/clients` - Create client
|
||||||
- `PUT /api/clients/:id` - Update client
|
- `PUT /api/clients/:id` - Update client
|
||||||
- `DELETE /api/clients/:id` - Delete client
|
- `DELETE /api/clients/:id` - Delete client
|
||||||
|
|
||||||
### Projects
|
### Projects
|
||||||
|
|
||||||
- `GET /api/projects` - List projects
|
- `GET /api/projects` - List projects
|
||||||
- `POST /api/projects` - Create project
|
- `POST /api/projects` - Create project
|
||||||
- `PUT /api/projects/:id` - Update project
|
- `PUT /api/projects/:id` - Update project
|
||||||
- `DELETE /api/projects/:id` - Delete project
|
- `DELETE /api/projects/:id` - Delete project
|
||||||
|
|
||||||
### Time Entries
|
### Time Entries
|
||||||
|
|
||||||
- `GET /api/time-entries` - List entries (with filters/pagination)
|
- `GET /api/time-entries` - List entries (with filters/pagination)
|
||||||
- `POST /api/time-entries` - Create entry
|
- `POST /api/time-entries` - Create entry
|
||||||
- `PUT /api/time-entries/:id` - Update entry
|
- `PUT /api/time-entries/:id` - Update entry
|
||||||
- `DELETE /api/time-entries/:id` - Delete entry
|
- `DELETE /api/time-entries/:id` - Delete entry
|
||||||
|
|
||||||
### Timer
|
### Timer
|
||||||
|
|
||||||
- `GET /api/timer` - Get ongoing timer
|
- `GET /api/timer` - Get ongoing timer
|
||||||
- `POST /api/timer/start` - Start timer
|
- `POST /api/timer/start` - Start timer
|
||||||
- `PUT /api/timer` - Update timer (set project)
|
- `PUT /api/timer` - Update timer (set project)
|
||||||
@@ -134,6 +139,7 @@ User (oidc sub)
|
|||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
**Backend:**
|
**Backend:**
|
||||||
|
|
||||||
- Node.js + Express
|
- Node.js + Express
|
||||||
- TypeScript
|
- TypeScript
|
||||||
- Prisma ORM
|
- Prisma ORM
|
||||||
@@ -141,6 +147,7 @@ User (oidc sub)
|
|||||||
- OpenID Client
|
- OpenID Client
|
||||||
|
|
||||||
**Frontend:**
|
**Frontend:**
|
||||||
|
|
||||||
- React 18
|
- React 18
|
||||||
- TypeScript
|
- TypeScript
|
||||||
- TanStack Query
|
- TanStack Query
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export const config = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
cors: {
|
cors: {
|
||||||
origin: process.env.FRONTEND_URL || "http://localhost:5173",
|
origin: process.env.APP_URL || "http://localhost:5173",
|
||||||
credentials: true,
|
credentials: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ router.get("/callback", async (req, res) => {
|
|||||||
delete req.session.oidc;
|
delete req.session.oidc;
|
||||||
|
|
||||||
// Redirect to frontend
|
// Redirect to frontend
|
||||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173";
|
const frontendUrl = process.env.APP_URL || "http://localhost:5173";
|
||||||
res.redirect(`${frontendUrl}/auth/callback?success=true`);
|
res.redirect(`${frontendUrl}/auth/callback?success=true`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Callback error:", error);
|
console.error("Callback error:", error);
|
||||||
const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5173";
|
const frontendUrl = process.env.APP_URL || "http://localhost:5173";
|
||||||
res.redirect(`${frontendUrl}/auth/callback?error=authentication_failed`);
|
res.redirect(`${frontendUrl}/auth/callback?error=authentication_failed`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
version: '3.8'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -25,11 +23,11 @@ services:
|
|||||||
DATABASE_URL: "postgresql://timetracker:timetracker_password@db:5432/timetracker"
|
DATABASE_URL: "postgresql://timetracker:timetracker_password@db:5432/timetracker"
|
||||||
OIDC_ISSUER_URL: ${OIDC_ISSUER_URL}
|
OIDC_ISSUER_URL: ${OIDC_ISSUER_URL}
|
||||||
OIDC_CLIENT_ID: ${OIDC_CLIENT_ID}
|
OIDC_CLIENT_ID: ${OIDC_CLIENT_ID}
|
||||||
OIDC_REDIRECT_URI: "http://localhost:3001/api/auth/callback"
|
OIDC_REDIRECT_URI: "${API_URL}/auth/callback"
|
||||||
SESSION_SECRET: ${SESSION_SECRET}
|
SESSION_SECRET: ${SESSION_SECRET}
|
||||||
PORT: 3001
|
PORT: 3001
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
FRONTEND_URL: "http://localhost:5173"
|
APP_URL: "${APP_URL}"
|
||||||
ports:
|
ports:
|
||||||
- "3001:3001"
|
- "3001:3001"
|
||||||
depends_on:
|
depends_on:
|
||||||
@@ -40,8 +38,8 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: ./frontend
|
context: ./frontend
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
environment:
|
args:
|
||||||
VITE_API_URL: "http://localhost:3001"
|
- VITE_API_URL=${API_URL}
|
||||||
ports:
|
ports:
|
||||||
- "5173:80"
|
- "5173:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# Build stage
|
# Build stage
|
||||||
FROM node:20-alpine as builder
|
FROM node:20-alpine as builder
|
||||||
|
|
||||||
|
ARG VITE_API_URL
|
||||||
|
ENV VITE_API_URL=${VITE_API_URL}
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package*.json ./
|
COPY package*.json ./
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import type { User } from "@/types";
|
import type { User } from "@/types";
|
||||||
|
|
||||||
const AUTH_BASE = "/api/auth";
|
const AUTH_BASE = import.meta.env.VITE_API_URL + "/auth";
|
||||||
|
|
||||||
export const authApi = {
|
export const authApi = {
|
||||||
login: (): void => {
|
login: (): void => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: "/api",
|
baseURL: import.meta.env.VITE_API_URL,
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
|
|||||||
13
frontend/src/vite-env.d.ts
vendored
Normal file
13
frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
interface ViteTypeOptions {
|
||||||
|
// By adding this line, you can make the type of ImportMetaEnv strict
|
||||||
|
// to disallow unknown keys.
|
||||||
|
// strictImportMetaEnv: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
VITE_API_URL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
@@ -2,9 +2,8 @@ import { defineConfig } from "vite";
|
|||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const backend = "http://127.0.0.1:3001";
|
export default defineConfig(() => {
|
||||||
|
return {
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@@ -13,11 +12,6 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
proxy: {
|
|
||||||
"/api": {
|
|
||||||
target: backend,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user