prepped files for container deployment
This commit is contained in:
parent
f90435399d
commit
a991d18685
@ -65,7 +65,7 @@ function App() {
|
||||
};
|
||||
|
||||
const fetchArticles = () => {
|
||||
fetch('http://localhost:9000/api/articles', {
|
||||
fetch('/api/articles', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
@ -97,7 +97,7 @@ function App() {
|
||||
fetchArticles();
|
||||
}
|
||||
|
||||
fetch(`http://localhost:9000/api/search/advanced?${params.toString()}`, {
|
||||
fetch(`/api/search/advanced?${params.toString()}`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
@ -130,7 +130,7 @@ function App() {
|
||||
const isNew = !updatedArticle.ka_number || updatedArticle.ka_number === 'New Article';
|
||||
|
||||
if (isNew) {
|
||||
fetch('http://localhost:9000/api/articles', {
|
||||
fetch('/api/articles', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -146,7 +146,7 @@ function App() {
|
||||
})
|
||||
.catch(error => console.error('Error creating article:', error));
|
||||
} else {
|
||||
fetch(`http://localhost:9000/api/articles/${updatedArticle.ka_number}`, {
|
||||
fetch(`/api/articles/${updatedArticle.ka_number}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -176,7 +176,7 @@ function App() {
|
||||
|
||||
const handleCreateNew = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/articles', {
|
||||
const response = await fetch('/api/articles', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
@ -205,7 +205,7 @@ function App() {
|
||||
|
||||
const handleDelete = () => {
|
||||
if (confirm('Are you sure you want to delete this article?')) {
|
||||
fetch(`http://localhost:9000/api/articles/${selectedArticle.ka_number}`, {
|
||||
fetch(`/api/articles/${selectedArticle.ka_number}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
})
|
||||
|
||||
@ -14,7 +14,7 @@ function AdminPanel({ token, onBack }) {
|
||||
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/admin/users', {
|
||||
const response = await fetch('/api/admin/users', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
@ -41,7 +41,7 @@ function AdminPanel({ token, onBack }) {
|
||||
|
||||
const handleSaveRole = async (userId) => {
|
||||
try {
|
||||
const response = await fetch(`http://localhost:9000/api/admin/users/${userId}/role`, {
|
||||
const response = await fetch(`/api/admin/users/${userId}/role`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
@ -76,7 +76,7 @@ function AdminPanel({ token, onBack }) {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:9000/api/admin/users/${userId}`, {
|
||||
const response = await fetch(`/api/admin/users/${userId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
|
||||
@ -125,7 +125,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
|
||||
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/categories', {
|
||||
const response = await fetch('/api/categories', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
@ -142,7 +142,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
|
||||
|
||||
const fetchTags = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/tags', {
|
||||
const response = await fetch('/api/tags', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
@ -171,7 +171,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
|
||||
onSave(articleData);
|
||||
|
||||
try {
|
||||
await fetch(`http://localhost:9000/api/articles/${article.ka_number}/categories`, {
|
||||
await fetch(`/api/articles/${article.ka_number}/categories`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
@ -180,7 +180,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
|
||||
body: JSON.stringify({ categories: selectedCategories })
|
||||
});
|
||||
|
||||
await fetch(`http://localhost:9000/api/articles/${article.ka_number}/tags`, {
|
||||
await fetch(`/api/articles/${article.ka_number}/tags`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
@ -222,7 +222,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
|
||||
if (!quill) return;
|
||||
|
||||
const range = quill.getSelection(true);
|
||||
const mediaUrl = `http://localhost:9000${mediaItem.url}`;
|
||||
const mediaUrl = `/${mediaItem.url}`;
|
||||
|
||||
if (mediaItem.type === 'image') {
|
||||
quill.insertEmbed(range.index, 'image', mediaUrl);
|
||||
@ -241,7 +241,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
|
||||
if (!newCategoryInput.trim()) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/categories', {
|
||||
const response = await fetch('/api/categories', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
|
||||
@ -12,7 +12,7 @@ function DraftsList({ token, onBack, onSelectDraft }) {
|
||||
|
||||
const fetchDrafts = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/articles/drafts', {
|
||||
const response = await fetch('\/api/articles/drafts', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
@ -38,7 +38,7 @@ function DraftsList({ token, onBack, onSelectDraft }) {
|
||||
if(!confirm('Are you sure you want to delete this draft?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:9000/api/articles/${kaNumber}`, {
|
||||
const response = await fetch(`/api/articles/${kaNumber}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
|
||||
@ -30,7 +30,7 @@ function FilterBar({ token, onFilterChange }) {
|
||||
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/categories', {
|
||||
const response = await fetch('/api/categories', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
@ -47,7 +47,7 @@ function FilterBar({ token, onFilterChange }) {
|
||||
|
||||
const fetchTags = async () => {
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/tags', {
|
||||
const response = await fetch('/api/tags', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ function Login({ onLoginSuccess, onSwitchToRegister }) {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/auth/login', {
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
@ -76,7 +76,7 @@ function Login({ onLoginSuccess, onSwitchToRegister }) {
|
||||
const loginResponse = await msalInstance.loginPopup(loginRequest);
|
||||
|
||||
// Send the access token to your backend
|
||||
const response = await fetch('http://localhost:9000/api/auth/microsoft', {
|
||||
const response = await fetch('/api/auth/microsoft', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@ -44,7 +44,7 @@ function MediaGallery({ kaNumber, token, onInsertMedia, isOpen, onToggle }) {
|
||||
formData.append('file', file);
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:9000/api/articles/${kaNumber}/media`, {
|
||||
const response = await fetch(`/api/articles/${kaNumber}/media`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
@ -72,7 +72,7 @@ function MediaGallery({ kaNumber, token, onInsertMedia, isOpen, onToggle }) {
|
||||
if (!confirm('Are you sure you want to delete this file?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`http://localhost:9000/api/articles/${kaNumber}/media/${filename}`, {
|
||||
const response = await fetch(`/api/articles/${kaNumber}/media/${filename}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
@ -139,7 +139,7 @@ function MediaGallery({ kaNumber, token, onInsertMedia, isOpen, onToggle }) {
|
||||
<div key={item.filename} className='media-item'>
|
||||
{item.type === 'image' ? (
|
||||
<img
|
||||
src={`http://localhost:9000${item.url}`}
|
||||
src={`${item.url}`}
|
||||
alt={item.filename}
|
||||
onClick={() => onInsertMedia(item)}
|
||||
/>
|
||||
|
||||
@ -27,7 +27,7 @@ function Registration({ onRegistrationSuccess, onSwitchToLogin }) {
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('http://localhost:9000/api/auth/register', {
|
||||
const response = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
39
docker-compose.yml
Normal file
39
docker-compose.yml
Normal file
@ -0,0 +1,39 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
knowledge-base:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: knowledge-base
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=9000
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
- MICROSOFT_CLIENT_ID=${MICROSOFT_CLIENT_ID}
|
||||
- MICROSOFT_CLIENT_SECRET=${MICROSOFT_CLIENT_SECRET}
|
||||
- MICROSOFT_TENANT_ID=${MICROSOFT_TENANT_ID}
|
||||
volumes:
|
||||
- kb-data:/app/kb.db
|
||||
- kb-media:/app/media
|
||||
networks:
|
||||
- traefik
|
||||
labels:
|
||||
# Traefik routing
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.kb.rule=Host(`kb.jv.com`)"
|
||||
- "traefik.http.routers.kb.entrypoints=websecure"
|
||||
- "traefik.http.routers.kb.tls=true"
|
||||
- "traefik.http.services.kb.loadbalancer.server.port=9000"
|
||||
- "traefik.docker.network=traefik_traefik-proxy"
|
||||
|
||||
networks:
|
||||
traefik:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
kb-data:
|
||||
driver: local
|
||||
kb-media:
|
||||
driver: local
|
||||
23
dockerfile
Normal file
23
dockerfile
Normal file
@ -0,0 +1,23 @@
|
||||
# Combined Frontend + Backend Dockerfile
|
||||
# Multi-stage build
|
||||
|
||||
FROM node:20-slim AS frontend-build
|
||||
WORKDIR /app/frontend
|
||||
COPY client/kb-frontend/package*.json ./
|
||||
RUN npm ci
|
||||
COPY client/kb-frontend ./
|
||||
RUN npm run build
|
||||
FROM node:20-slim
|
||||
RUN apt-get update && apt-get install -y \
|
||||
python3 \
|
||||
make \
|
||||
g++ \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /app
|
||||
COPY server/package*.json ./
|
||||
RUN npm ci --only=production
|
||||
COPY server/ ./
|
||||
COPY --from=frontend-build /app/frontend/dist ./frontend/dist
|
||||
RUN mkdir -p /app/media
|
||||
EXPOSE 9000
|
||||
CMD ["node", "server.js"]
|
||||
@ -1,7 +1,7 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const {getUserById} = require('./db');
|
||||
|
||||
const JWT_SECRET = 'THISISTOTALLYSECRETDONTLOOK';
|
||||
const JWT_SECRET = 'T0JcuWfi3A2GdRd71FnBmH2c16guYVT/d3Jo1I9hdcs=';
|
||||
const JWT_EXPIRATION = '24h';
|
||||
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ const PORT = 9000;
|
||||
app.use(cors())
|
||||
app.use(express.json());
|
||||
|
||||
const msalClient = new msal.ConfidentialClientApplication(entraConfig);
|
||||
|
||||
const upload = multer({
|
||||
dest: './temp_uploads/',
|
||||
limits: {
|
||||
@ -56,6 +56,12 @@ if (!fs.existsSync('./media')) {
|
||||
|
||||
initDb().then(() => {
|
||||
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/articles', articleRoutes);
|
||||
app.use('/api/admin', adminRoutes);
|
||||
app.use('/api/categories', categoryRoutes);
|
||||
app.use('/api/tags', tagRoutes);
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.json({ message: 'Server is running' });
|
||||
});
|
||||
@ -666,8 +672,14 @@ initDb().then(() => {
|
||||
}
|
||||
});
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'frontend/dist')));
|
||||
app.use('/media', express.static(path.join(__dirname, 'media')));
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(__dirname, 'frontend/dist', 'index.html'));
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
console.log(`Server listening on ${PORT}`);
|
||||
});
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('Failed to initialize database:', err);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user