diff --git a/backend/.gitignore b/backend/.gitignore index 8f31ed2..efcb4ca 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,4 @@ node_modules/ .env -public/uploads/ +public/uploads/* +!public/uploads/dummy.txt diff --git a/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js b/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js new file mode 100644 index 0000000..5f615b2 --- /dev/null +++ b/backend/migrations/20250712000000-rename-path-to-filename-in-attachments.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + up: async (queryInterface) => { + // Rename column 'path' to 'filename' in 'Attachments' table + await queryInterface.renameColumn('Attachments', 'path', 'filename'); + }, + + down: async (queryInterface) => { + // Revert column name from 'filename' back to 'path' + await queryInterface.renameColumn('Attachments', 'filename', 'path'); + }, +}; diff --git a/backend/models/attachments.js b/backend/models/attachments.js index 7be6eb5..2f47dc2 100644 --- a/backend/models/attachments.js +++ b/backend/models/attachments.js @@ -8,7 +8,7 @@ function defineAttachment(sequelize, DataTypes) { type: DataTypes.STRING, allowNull: true, }, - path: { + filename: { type: DataTypes.STRING, allowNull: false, }, diff --git a/backend/public/uploads/dummy.txt b/backend/public/uploads/dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/backend/routes/attachments/delete.js b/backend/routes/attachments/delete.js index 9bfee9b..2ded08e 100644 --- a/backend/routes/attachments/delete.js +++ b/backend/routes/attachments/delete.js @@ -1,9 +1,9 @@ +const fs = require('fs'); +const path = require('path'); const express = require('express'); const router = express.Router(); -const path = require('path'); -const fs = require('fs'); -const defineAttachment = require('../../models/attachments'); const { DataTypes } = require('sequelize'); +const defineAttachment = require('../../models/attachments'); module.exports = function (sequelize) { const Attachment = defineAttachment(sequelize, DataTypes); @@ -21,9 +21,7 @@ module.exports = function (sequelize) { // delete file from folder const uploadDir = path.join(__dirname, '../../public/uploads'); - const url = attachment.path; - const fileName = url.substring(url.lastIndexOf('/') + 1); - const filePath = path.join(uploadDir, fileName); + const filePath = path.join(uploadDir, attachment.filename); fs.unlink(filePath, (err) => { if (err) { console.error('Error deleting file:', err); diff --git a/backend/routes/attachments/download.js b/backend/routes/attachments/download.js index 35c9270..c2d8d28 100644 --- a/backend/routes/attachments/download.js +++ b/backend/routes/attachments/download.js @@ -1,9 +1,9 @@ +const fs = require('fs'); +const path = require('path'); const express = require('express'); const router = express.Router(); -const path = require('path'); -const fs = require('fs'); -const defineAttachment = require('../../models/attachments'); const { DataTypes } = require('sequelize'); +const defineAttachment = require('../../models/attachments'); module.exports = function (sequelize) { const Attachment = defineAttachment(sequelize, DataTypes); @@ -16,8 +16,7 @@ module.exports = function (sequelize) { return res.status(404).send('Attachment not found'); } - const filename = attachment.path.split('/').pop(); - const filePath = path.join(__dirname, `../../public/uploads/${filename}`); + const filePath = path.join(__dirname, `../../public/uploads/${attachment.filename}`); if (!fs.existsSync(filePath)) { return res.status(404).json({ error: 'File not found' }); diff --git a/backend/routes/attachments/new.js b/backend/routes/attachments/new.js index 2c60cc2..2771a60 100644 --- a/backend/routes/attachments/new.js +++ b/backend/routes/attachments/new.js @@ -1,11 +1,11 @@ +const fs = require('fs'); +const path = require('path'); const express = require('express'); const router = express.Router(); -const path = require('path'); -const fs = require('fs'); const multer = require('multer'); +const { DataTypes } = require('sequelize'); const defineAttachment = require('../../models/attachments'); const defineCaseAttachment = require('../../models/caseAttachments'); -const { DataTypes } = require('sequelize'); module.exports = function (sequelize) { const Attachment = defineAttachment(sequelize, DataTypes); @@ -55,11 +55,9 @@ module.exports = function (sequelize) { return res.status(400).json({ error: 'No files uploaded' }); } - const host = req.get('host'); - const protocol = req.protocol; const attachmentsData = files.map((file) => ({ title: file.originalname, - path: `${protocol}://${host}/uploads/${file.filename}`, + filename: file.filename, })); const newAttachments = await Attachment.bulkCreate(attachmentsData, { @@ -74,6 +72,7 @@ module.exports = function (sequelize) { await t.commit(); res.json(newAttachments); } catch (error) { + console.error(error); await t.rollback(); res.status(500).json({ error: 'Internal server error' }); } diff --git a/backend/routes/health/index.js b/backend/routes/health/index.js new file mode 100644 index 0000000..6252d2c --- /dev/null +++ b/backend/routes/health/index.js @@ -0,0 +1,10 @@ +const express = require('express'); +const router = express.Router(); + +module.exports = function () { + router.get('/', async (req, res) => { + res.json({ status: 'ok' }); + }); + + return router; +}; diff --git a/backend/routes/index.js b/backend/routes/index.js index 5ca1411..8424234 100644 --- a/backend/routes/index.js +++ b/backend/routes/index.js @@ -3,7 +3,7 @@ const router = express.Router(); // "/" GET router.get('/', (req, res) => { - res.send('Test Case Management API Server'); + res.send('This is UnitTCMS API server'); }); module.exports = router; diff --git a/backend/seeders/seed.js b/backend/seeders/seed.js index 6206161..83efa5c 100644 --- a/backend/seeders/seed.js +++ b/backend/seeders/seed.js @@ -550,19 +550,18 @@ module.exports = { }, ]); - const backendOrigin = process.env.BACKEND_ORIGIN || 'http://localhost:8001'; await queryInterface.bulkInsert('attachments', [ { title: 'Selenium logo', detail: '', - path: `${backendOrigin}/uploads/861px-Selenium_Logo.png`, + filename: '861px-Selenium_Logo.png', createdAt: new Date(), updatedAt: new Date(), }, { title: 'vitest logo', detail: '', - path: `${backendOrigin}/uploads/logo-shadow.svg`, + filename: 'logo-shadow.svg', createdAt: new Date(), updatedAt: new Date(), }, diff --git a/backend/server.js b/backend/server.js index 99270c6..062ef73 100644 --- a/backend/server.js +++ b/backend/server.js @@ -39,6 +39,10 @@ const sequelize = new Sequelize({ const indexRoute = require('./routes/index'); app.use('/', indexRoute); +// "/health" +const healthIndexRoute = require('./routes/health/index')(); +app.use('/health', healthIndexRoute); + // "users" const usersIndexRoute = require('./routes/users/index')(sequelize); const usersFindRoute = require('./routes/users/find')(sequelize); diff --git a/docker-compose.yaml b/docker-compose.yaml index b6e2ac5..74cfdf3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,6 +10,7 @@ services: - IS_DEMO=false # set to true to seed the database volumes: - db-data:/app/backend/database + - ./backend/public/uploads:/app/backend/public/uploads volumes: db-data: diff --git a/frontend/components/Footer.tsx b/frontend/components/Footer.tsx new file mode 100644 index 0000000..649c096 --- /dev/null +++ b/frontend/components/Footer.tsx @@ -0,0 +1,18 @@ +import { Link, NextUiLinkClasses } from '@/src/i18n/routing'; +import { LocaleCodeType } from '@/types/locale'; + +type Props = { + locale: LocaleCodeType; +}; + +export default function Footer({ locale }: Props) { + return ( +
+
Copyright © 2024-present UnitTCMS
+ + + Status + +
+ ); +} diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 409a0e4..aebb48e 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -114,6 +114,14 @@ "not_own_any_projects": "You don't own any projects.", "find_projects": "Find projects" }, + "Health": { + "health_check": "Health Check", + "status": "Status", + "ok": "OK", + "error": "Error", + "api_server": "API Server", + "unittcms_version": "UnitTCMS Version" + }, "Admin": { "user_management": "User Management", "avatar": "Avatar", diff --git a/frontend/messages/ja.json b/frontend/messages/ja.json index ccd1632..621bede 100644 --- a/frontend/messages/ja.json +++ b/frontend/messages/ja.json @@ -133,6 +133,14 @@ "lost_admin_auth": "管理者権限を失いました", "at_least": "最低1人以上の管理者が必要です。" }, + "Health": { + "health_check": "ヘルスチェック", + "status": "ステータス", + "ok": "正常", + "error": "エラー", + "api_server": "APIサーバー", + "unittcms_version": "UnitTCMS バージョン" + }, "Projects": { "project_list": "プロジェクト一覧", "new_project": "新規プロジェクト", diff --git a/frontend/src/app/[locale]/HeaderNavbarMenu.tsx b/frontend/src/app/[locale]/HeaderNavbarMenu.tsx index c8ff589..dc2f90b 100644 --- a/frontend/src/app/[locale]/HeaderNavbarMenu.tsx +++ b/frontend/src/app/[locale]/HeaderNavbarMenu.tsx @@ -97,13 +97,6 @@ export default function HeaderNavbarMenu({ messages, locale }: Props) {

UnitTCMS

- - - - 1.0.0-beta.14 - - - {commonLinks.map((link) => link.isExternal ? ( diff --git a/frontend/src/app/[locale]/LandingPage.tsx b/frontend/src/app/[locale]/LandingPage.tsx index 0dd7701..584a4bf 100644 --- a/frontend/src/app/[locale]/LandingPage.tsx +++ b/frontend/src/app/[locale]/LandingPage.tsx @@ -6,6 +6,7 @@ import DemoImage from './DemoImage'; import { title, subtitle } from '@/components/primitives'; import { PageType } from '@/types/base'; import { LocaleCodeType } from '@/types/locale'; +import Footer from '@/components/Footer'; export default function LandingPage({ params }: PageType) { const t = useTranslations('Index'); @@ -98,9 +99,7 @@ export default function LandingPage({ params }: PageType) { -
-
Copyright © 2024-present UnitTCMS
-
+