利用規約の追加とエラーハンドリング、トップページの修正

This commit is contained in:
ntki72 2024-12-26 22:14:43 +09:00
parent c804494541
commit 29cebe239f
22 changed files with 540 additions and 214 deletions

View File

@ -16,6 +16,7 @@ services:
TZ: Asia/Tokyo TZ: Asia/Tokyo
volumes: volumes:
- ./public/thumbnails:/app/public/thumbnails - ./public/thumbnails:/app/public/thumbnails
- ./public/css:/app/public/css
db: db:
image: mysql:8.0 image: mysql:8.0
volumes: volumes:

31
public/css/errors.css Normal file
View File

@ -0,0 +1,31 @@
.error-container {
text-align: center;
margin-top: 100px;
font-family: Arial, sans-serif;
}
.error-container h1 {
font-size: 5em;
margin-bottom: 20px;
color: #ff6b6b;
}
.error-container p {
font-size: 1.5em;
margin-bottom: 30px;
color: #333;
}
.home-link {
display: inline-block;
padding: 10px 20px;
background-color: #007bff;
color: #fff;
text-decoration: none;
border-radius: 5px;
font-size: 1em;
}
.home-link:hover {
background-color: #0056b3;
}

48
public/css/header.css Normal file
View File

@ -0,0 +1,48 @@
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
background-color: #333;
color: white;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
box-sizing: border-box;
}
.header-left a {
color: white;
font-size: 1.5em;
font-weight: bold;
text-decoration: none;
white-space: nowrap;
}
.header-right {
display: flex;
gap: 20px;
margin-right: 10px;
white-space: nowrap;
}
.header-link {
color: white;
text-decoration: none;
font-size: 1em;
transition: color 0.2s;
}
.header-link:hover {
color: #ddd;
}
@media (max-width: 768px) {
.header-right {
margin-right: 5px;
}
}

View File

@ -1,59 +1,69 @@
.schedule-container { .schedule-container {
width: 80%; width: 80%;
margin: 0 auto; margin: 0 auto;
} }
.date-header { .date-header {
font-size: 1.5em; font-size: 1.5em;
margin: 20px 0; margin: 20px 0;
font-weight: bold; font-weight: bold;
} min-width: 550px;
}
.banners {
display: flex; .banners {
flex-wrap: wrap; display: flex;
gap: 20px; flex-wrap: wrap;
} gap: 20px;
}
.banner { .banner {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
text-decoration: none; text-decoration: none;
color: black; color: black;
background-color: #f9f9f9; background-color: #f9f9f9;
border: 1px solid #ddd; border: 1px solid #ddd;
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
width: calc(50% - 10px); /* 2列になるように調整 */ flex: 1 1 calc(50% - 13px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); max-width: calc(50% - 13px);
transition: transform 0.2s, box-shadow 0.2s; min-width: 550px;
} box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
.banner:hover { }
transform: scale(1.02);
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15); .banner:hover {
} transform: scale(1.02);
box-shadow: 0 6px 10px rgba(0, 0, 0, 0.15);
.thumbnail img { }
width: 150px;
height: auto; .thumbnail img {
object-fit: cover; width: 150px;
} height: auto;
object-fit: cover;
.details { }
padding: 10px;
flex: 1; .details {
} padding: 10px;
flex: 1;
.time-channel { }
font-size: 1em;
margin-bottom: 5px; .time-channel {
color: #333; font-size: 1em;
} margin-bottom: 5px;
color: #333;
.title { }
font-size: 1.2em;
font-weight: bold; .title {
color: #555; font-size: 1.2em;
} font-weight: bold;
color: #555;
}
.header-spacer {
height: 60px;
}
.main-content {
margin-top: 20px;
}

47
public/css/terms.css Normal file
View File

@ -0,0 +1,47 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.terms-container {
max-width: 800px;
margin: 80px auto; /* ヘッダーの高さを考慮して調整 */
padding: 20px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2em;
margin-bottom: 20px;
text-align: center;
color: #333333;
}
h2 {
font-size: 1.5em;
margin-top: 20px;
margin-bottom: 10px;
color: #444444;
border-bottom: 2px solid #ddd;
padding-bottom: 5px;
}
p {
font-size: 1em;
line-height: 1.6;
margin-bottom: 15px;
color: #555555;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}

View File

@ -3,6 +3,7 @@ import cron from "node-cron";
import express from "express"; import express from "express";
import apiRoutes from "./routes/api"; import apiRoutes from "./routes/api";
import adminRoutes from "./routes/admin"; import adminRoutes from "./routes/admin";
import userRoutes from "./routes/user";
import basicAuth from "express-basic-auth"; import basicAuth from "express-basic-auth";
import { runMigrations } from "./utils/migrate"; import { runMigrations } from "./utils/migrate";
import methodOverride from "method-override"; import methodOverride from "method-override";
@ -46,16 +47,33 @@ const startServer = async () => {
// 管理画面ルート // 管理画面ルート
app.use("/admin", adminRoutes); app.use("/admin", adminRoutes);
// ユーザー向けルート
app.use("/", userRoutes);
// 管理画面 API // 管理画面 API
app.use("/admin/api", apiRoutes); app.use("/admin/api", apiRoutes);
// ユーザー向けルート
// トップページ
app.use("/", scheduleRoutes);
// 静的ファイルの提供 // 静的ファイルの提供
app.use("/thumbnails", express.static("public/thumbnails")); app.use("/thumbnails", express.static("public/thumbnails"));
app.use("/css", express.static("public/css")); app.use("/css", express.static("public/css"));
app.use("/js", express.static("public/js"));
// エラーハンドリング
app.use((req, res, next) => {
res.status(404).render("errors/404");
});
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error("Unhandled error:", err);
if (err.message.includes("permission")) {
res.status(403).render("errors/403");
} else if (res.statusCode === 503) {
res.status(503).render("errors/503");
} else {
res.status(500).render("errors/500");
}
});
// サーバー起動 // サーバー起動
app.listen(WEB_PORT, () => { app.listen(WEB_PORT, () => {

View File

@ -1,5 +1,6 @@
import express from "express"; import express from "express";
import { LiveSchedule, Channel } from "../models"; import { LiveSchedule, Channel } from "../models";
import { Op } from "sequelize";
import dayjs from "dayjs"; import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone"; import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc"; import utc from "dayjs/plugin/utc";
@ -11,8 +12,14 @@ const router = express.Router();
router.get("/", async (req, res) => { router.get("/", async (req, res) => {
try { try {
const now = dayjs().tz("Asia/Tokyo").startOf("day"); // 当日0時
const liveSchedules = await LiveSchedule.findAll({ const liveSchedules = await LiveSchedule.findAll({
include: [{ model: Channel }], include: [{ model: Channel }],
where: {
start_time: {
[Op.gte]: now.toDate(), // Sequelize の Op を使用
},
},
order: [["start_time", "ASC"]], order: [["start_time", "ASC"]],
}); });

14
src/routes/user.ts Normal file
View File

@ -0,0 +1,14 @@
import express from "express";
import scheduleRoutes from "./schedule";
const router = express.Router();
// スケジュールページ(トップページ)
router.use("/", scheduleRoutes);
// 利用規約ページ
router.get("/terms", (req, res) => {
res.render("terms");
});
export default router;

View File

@ -1,49 +1,49 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Channel List</title> </head>
</head> <%- include("../partials/head") %>
<body> <body>
<h1>Channel List</h1> <h1>Channel List</h1>
<a href="/admin/channels/new">Add New Channel</a> <a href="/admin/channels/new">Add New Channel</a>
<table border="1"> <table border="1">
<thead> <thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Channel Handle</th>
<th>YouTube ID</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<% channels.forEach(channel => { %>
<tr> <tr>
<td><%= channel.id %></td> <th>ID</th>
<td><%= channel.name %></td> <th>Name</th>
<td><%= channel.channel_handle %></td> <th>Channel Handle</th>
<td><%= channel.youtube_id %></td> <th>YouTube ID</th>
<td> <th>Actions</th>
<form action="/admin/channels/<%= channel.id %>/edit" method="GET" style="display:inline;">
<button type="submit">Edit</button>
</form>
<form action="/admin/channels/<%= channel.id %>?_method=DELETE" method="POST" style="display:inline;">
<button type="submit" onclick="return confirm('Are you sure you want to delete this channel?');">Delete</button>
</form>
<form action="/admin/channels/<%= channel.id %>/run" method="GET" style="display:inline;">
<button type="submit">Run</button>
</form>
<form action="/admin/channels/<%= channel.id %>/fetch-history" method="POST" style="display:inline;">
<input type="date" name="startDate" required>
<input type="date" name="endDate" required>
<button type="submit">Fetch History</button>
</form>
</td>
</tr> </tr>
<% }) %> </thead>
</tbody> <tbody>
</table> <% channels.forEach(channel => { %>
</body> <tr>
<td><%= channel.id %></td>
<td><%= channel.name %></td>
<td><%= channel.channel_handle %></td>
<td><%= channel.youtube_id %></td>
<td>
<form action="/admin/channels/<%= channel.id %>/edit" method="GET" style="display:inline;">
<button type="submit">Edit</button>
</form>
<form action="/admin/channels/<%= channel.id %>?_method=DELETE" method="POST" style="display:inline;">
<button type="submit" onclick="return confirm('Are you sure you want to delete this channel?');">Delete</button>
</form>
<form action="/admin/channels/<%= channel.id %>/run" method="GET" style="display:inline;">
<button type="submit">Run</button>
</form>
<form action="/admin/channels/<%= channel.id %>/fetch-history" method="POST" style="display:inline;">
<input type="date" name="startDate" required>
<input type="date" name="endDate" required>
<button type="submit">Fetch History</button>
</form>
</td>
</tr>
<% }) %>
</tbody>
</table>
</body>
</html> </html>

View File

@ -1,23 +1,23 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title> </head>
</head> <%- include("../partials/head") %>
<body> <body>
<h1><%= title %></h1> <h1><%= title %></h1>
<form action="/admin/channels/<%= channel.id %>?_method=PUT" method="POST"> <form action="/admin/channels/<%= channel.id %>?_method=PUT" method="POST">
<label for="name">Channel Name:</label> <label for="name">Channel Name:</label>
<input type="text" id="name" name="name" value="<%= channel.name %>" required> <input type="text" id="name" name="name" value="<%= channel.name %>" required>
<br> <br>
<label for="channel_handle">Channel Handle:</label> <label for="channel_handle">Channel Handle:</label>
<input type="text" id="channel_handle" name="channel_handle" value="<%= channel.channel_handle %>" required> <input type="text" id="channel_handle" name="channel_handle" value="<%= channel.channel_handle %>" required>
<br> <br>
<label for="youtube_id">YouTube ID:</label> <label for="youtube_id">YouTube ID:</label>
<input type="text" id="youtube_id" name="youtube_id" value="<%= channel.youtube_id %>" required> <input type="text" id="youtube_id" name="youtube_id" value="<%= channel.youtube_id %>" required>
<br> <br>
<button type="submit">Update Channel</button> <button type="submit">Update Channel</button>
</form> </form>
</body> </body>
</html> </html>

View File

@ -1,12 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title> </head>
</head> <%- include("../partials/head") %>
<body> <body>
<h1><%= title %></h1> <h1><%= title %></h1>
<p>Welcome to the Admin Dashboard!</p> <p>Welcome to the Admin Dashboard!</p>
</body> </body>
</html> </html>

View File

@ -1,25 +1,25 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Add New Channel</title> </head>
</head> <%- include("../partials/head") %>
<body> <body>
<h1>Add New Channel</h1> <h1>Add New Channel</h1>
<form action="/admin/channels/new" method="POST"> <form action="/admin/channels/new" method="POST">
<label for="name">Channel Name (Optional):</label> <label for="name">Channel Name (Optional):</label>
<input type="text" id="name" name="name"> <input type="text" id="name" name="name">
<br> <br>
<label for="channel_handle">Channel Handle (e.g., @example):</label> <label for="channel_handle">Channel Handle (e.g., @example):</label>
<input type="text" id="channel_handle" name="channel_handle"> <input type="text" id="channel_handle" name="channel_handle">
<br> <br>
<label for="youtube_id">YouTube ID (e.g., UC_xxx...):</label> <label for="youtube_id">YouTube ID (e.g., UC_xxx...):</label>
<input type="text" id="youtube_id" name="youtube_id"> <input type="text" id="youtube_id" name="youtube_id">
<br> <br>
<p>Note: Either 'Channel Handle' or 'YouTube ID' must be provided.</p> <p>Note: Either 'Channel Handle' or 'YouTube ID' must be provided.</p>
<button type="submit">Add Channel</button> <button type="submit">Add Channel</button>
</form> </form>
<a href="/admin/channels">Back to Channel List</a> <a href="/admin/channels">Back to Channel List</a>
</body> </body>
</html> </html>

View File

@ -1,27 +1,27 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title> </head>
</head> <%- include("../partials/head") %>
<body> <body>
<h1><%= title %></h1> <h1><%= title %></h1>
<h2>Channel: <%= channel.name %> (<%= channel.youtube_id %>)</h2> <h2>Channel: <%= channel.name %> (<%= channel.youtube_id %>)</h2>
<h3>Live Streams</h3> <h3>Live Streams</h3>
<% if (liveStreams.length === 0) { %> <% if (liveStreams.length === 0) { %>
<p>No upcoming live streams found.</p> <p>No upcoming live streams found.</p>
<% } else { %> <% } else { %>
<ul> <ul>
<% liveStreams.forEach(stream => { %> <% liveStreams.forEach(stream => { %>
<li> <li>
<strong>Title:</strong> <%= stream.title %><br> <strong>Title:</strong> <%= stream.title %><br>
<strong>Start Time:</strong> <%= new Date(stream.start_time).toLocaleString() %><br> <strong>Start Time:</strong> <%= new Date(stream.start_time).toLocaleString() %><br>
<strong>Thumbnail:</strong> <img src="<%= stream.thumbnail_url %>" alt="Thumbnail" width="200"> <strong>Thumbnail:</strong> <img src="<%= stream.thumbnail_url %>" alt="Thumbnail" width="200">
</li> </li>
<% }) %> <% }) %>
</ul> </ul>
<% } %> <% } %>
<a href="/admin/channels">Back to Channel List</a> <a href="/admin/channels">Back to Channel List</a>
</body> </body>
</html> </html>

16
views/errors/400.ejs Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/errors.css">
</head>
<%- include("../partials/head") %>
<body>
<div class="error-container">
<h1>400</h1>
<p>送信されたリクエストに問題がありました。</p>
<a href="/" class="home-link">トップページへ戻る</a>
</div>
</body>
</html>

16
views/errors/403.ejs Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/errors.css">
</head>
<%- include("../partials/head") %>
<body>
<div class="error-container">
<h1>403</h1>
<p>このページへのアクセスが拒否されました。</p>
<a href="/" class="home-link">トップページへ戻る</a>
</div>
</body>
</html>

16
views/errors/404.ejs Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/errors.css">
</head>
<%- include("../partials/head") %>
<body>
<div class="error-container">
<h1>404</h1>
<p>お探しのページが見つかりませんでした。</p>
<a href="/" class="home-link">トップページへ戻る</a>
</div>
</body>
</html>

16
views/errors/500.ejs Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/errors.css">
</head>
<%- include("../partials/head") %>
<body>
<div class="error-container">
<h1>500</h1>
<p>予期せぬエラーが発生しました。少し時間をおいて再度お試しください。</p>
<a href="/" class="home-link">トップページへ戻る</a>
</div>
</body>
</html>

16
views/errors/503.ejs Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/errors.css">
</head>
<%- include("../partials/head") %>
<body>
<div class="error-container">
<h1>503</h1>
<p>現在サーバーが一時的に利用できません。時間をおいて再度お試しください。</p>
<a href="/" class="home-link">トップページへ戻る</a>
</div>
</body>
</html>

9
views/partials/head.ejs Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>神椿配信スケジュール</title>
<link rel="stylesheet" href="/css/header.css">
<link rel="stylesheet" href="/css/errors.css">
</head>

View File

@ -0,0 +1,8 @@
<header class="header">
<div class="header-left">
<a href="/">神椿配信スケジュール</a>
</div>
<div class="header-right">
<a href="/terms" class="header-link">利用規約</a>
</div>
</header>

View File

@ -1,34 +1,41 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ja"> <html lang="ja">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Schedule</title> <link rel="stylesheet" href="/css/schedule.css">
<link rel="stylesheet" href="/css/schedule.css"> <link rel="stylesheet" href="/css/header.css">
</head> </head>
<body> <%- include("partials/head") %>
<div class="schedule-container"> <body>
<% Object.keys(groupedSchedules).forEach(date => { %> <%- include("partials/header") %>
<div class="date-header"> <div class="header-spacer"></div>
<%= dayjs(date).tz("Asia/Tokyo").format("YYYY年MM月DD日 (ddd)") %> <div class="main-content">
</div> <div class="content">
<div class="banners"> <div class="schedule-container">
<% groupedSchedules[date].forEach(schedule => { %> <% Object.keys(groupedSchedules).forEach(date => { %>
<a href="https://www.youtube.com/watch?v=<%= schedule.video_id %>" class="banner" target="_blank"> <div class="date-header">
<div class="thumbnail"> <%= dayjs(date).tz("Asia/Tokyo").format("YYYY年MM月DD日 (ddd)") %>
<img src="<%= schedule.thumbnail_url || '/default-thumbnail.jpg' %>" alt="Thumbnail">
</div> </div>
<div class="details"> <div class="banners">
<div class="time-channel"> <% groupedSchedules[date].forEach(schedule => { %>
<span class="time"><%= dayjs(schedule.start_time).tz("Asia/Tokyo").format("HH:mm") %></span> <a href="https://www.youtube.com/watch?v=<%= schedule.video_id %>" class="banner" target="_blank">
<span class="channel-name">/ <%= schedule.Channel.name %></span> <div class="thumbnail">
</div> <img src="<%= schedule.thumbnail_url || '/default-thumbnail.jpg' %>" alt="Thumbnail">
<div class="title"><%= schedule.title %></div> </div>
<div class="details">
<div class="time-channel">
<span class="time"><%= dayjs(schedule.start_time).tz("Asia/Tokyo").format("HH:mm") %></span>
<span class="channel-name">/ <%= schedule.Channel.name %></span>
</div>
<div class="title"><%= schedule.title %></div>
</div>
</a>
<% }) %>
</div> </div>
</a> <% }) %>
<% }) %> </div>
</div> </div>
<% }) %> </div>
</div> </body>
</body>
</html> </html>

46
views/terms.ejs Normal file
View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/header.css">
<link rel="stylesheet" href="/css/terms.css">
</head>
<%- include("partials/head") %>
<body>
<%- include("partials/header") %>
<div class="terms-container">
<h1>利用規約</h1>
<section>
<h2>サイトの運営について</h2>
<p>
本サイトは、<strong>KAMITSUBAKI STUDIO</strong>およびその関連企業・団体とは一切関係のない非公式のウェブサイトです。<br>
本サイトは、個人が趣味の一環として運営しており、情報の正確性や完全性について保証するものではありません。
</p>
</section>
<section>
<h2>免責事項</h2>
<p>
本サイトに掲載されている情報は、できる限り正確であるよう努めていますが、その内容に誤りがある場合や、<br>
配信スケジュールの変更・キャンセル等に関して責任を負うものではありません。
</p>
<p>
また、本サイトを利用することで生じた損害について、一切の責任を負いません。利用者はご自身の責任において本サイトをご利用ください。<br>
</p>
</section>
<section>
<h2>著作権について</h2>
<p>
本サイトに掲載されている文章や画像などの著作物は、著作権法で認められる範囲内で利用していますが、<br>
著作権者からの要請があった場合、速やかに対応いたします。
</p>
</section>
<section>
<h2>お問い合わせ</h2>
<p>
本サイトに関するお問い合わせは、管理者宛てにご連絡ください。ただし、返信や対応を保証するものではありません。
</p>
</section>
</div>
</body>
</html>