利用規約の追加とエラーハンドリング、トップページの修正
This commit is contained in:
parent
c804494541
commit
29cebe239f
@ -16,6 +16,7 @@ services:
|
||||
TZ: Asia/Tokyo
|
||||
volumes:
|
||||
- ./public/thumbnails:/app/public/thumbnails
|
||||
- ./public/css:/app/public/css
|
||||
db:
|
||||
image: mysql:8.0
|
||||
volumes:
|
||||
|
31
public/css/errors.css
Normal file
31
public/css/errors.css
Normal 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
48
public/css/header.css
Normal 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;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
font-size: 1.5em;
|
||||
margin: 20px 0;
|
||||
font-weight: bold;
|
||||
min-width: 550px;
|
||||
}
|
||||
|
||||
.banners {
|
||||
@ -14,7 +15,6 @@
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.banner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -25,7 +25,9 @@
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
width: calc(50% - 10px); /* 2列になるように調整 */
|
||||
flex: 1 1 calc(50% - 13px);
|
||||
max-width: calc(50% - 13px);
|
||||
min-width: 550px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
@ -57,3 +59,11 @@
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.header-spacer {
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
47
public/css/terms.css
Normal file
47
public/css/terms.css
Normal 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;
|
||||
}
|
||||
|
26
src/app.ts
26
src/app.ts
@ -3,6 +3,7 @@ import cron from "node-cron";
|
||||
import express from "express";
|
||||
import apiRoutes from "./routes/api";
|
||||
import adminRoutes from "./routes/admin";
|
||||
import userRoutes from "./routes/user";
|
||||
import basicAuth from "express-basic-auth";
|
||||
import { runMigrations } from "./utils/migrate";
|
||||
import methodOverride from "method-override";
|
||||
@ -46,16 +47,33 @@ const startServer = async () => {
|
||||
// 管理画面ルート
|
||||
app.use("/admin", adminRoutes);
|
||||
|
||||
// ユーザー向けルート
|
||||
app.use("/", userRoutes);
|
||||
|
||||
// 管理画面 API
|
||||
app.use("/admin/api", apiRoutes);
|
||||
|
||||
// ユーザー向けルート
|
||||
// トップページ
|
||||
app.use("/", scheduleRoutes);
|
||||
|
||||
// 静的ファイルの提供
|
||||
app.use("/thumbnails", express.static("public/thumbnails"));
|
||||
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, () => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import express from "express";
|
||||
import { LiveSchedule, Channel } from "../models";
|
||||
import { Op } from "sequelize";
|
||||
import dayjs from "dayjs";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
@ -11,8 +12,14 @@ const router = express.Router();
|
||||
|
||||
router.get("/", async (req, res) => {
|
||||
try {
|
||||
const now = dayjs().tz("Asia/Tokyo").startOf("day"); // 当日0時
|
||||
const liveSchedules = await LiveSchedule.findAll({
|
||||
include: [{ model: Channel }],
|
||||
where: {
|
||||
start_time: {
|
||||
[Op.gte]: now.toDate(), // Sequelize の Op を使用
|
||||
},
|
||||
},
|
||||
order: [["start_time", "ASC"]],
|
||||
});
|
||||
|
||||
|
14
src/routes/user.ts
Normal file
14
src/routes/user.ts
Normal 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;
|
@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Channel List</title>
|
||||
</head>
|
||||
<%- include("../partials/head") %>
|
||||
<body>
|
||||
<h1>Channel List</h1>
|
||||
<a href="/admin/channels/new">Add New Channel</a>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %></title>
|
||||
</head>
|
||||
<%- include("../partials/head") %>
|
||||
<body>
|
||||
<h1><%= title %></h1>
|
||||
<form action="/admin/channels/<%= channel.id %>?_method=PUT" method="POST">
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %></title>
|
||||
</head>
|
||||
<%- include("../partials/head") %>
|
||||
<body>
|
||||
<h1><%= title %></h1>
|
||||
<p>Welcome to the Admin Dashboard!</p>
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Add New Channel</title>
|
||||
</head>
|
||||
<%- include("../partials/head") %>
|
||||
<body>
|
||||
<h1>Add New Channel</h1>
|
||||
<form action="/admin/channels/new" method="POST">
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %></title>
|
||||
</head>
|
||||
<%- include("../partials/head") %>
|
||||
<body>
|
||||
<h1><%= title %></h1>
|
||||
<h2>Channel: <%= channel.name %> (<%= channel.youtube_id %>)</h2>
|
||||
|
16
views/errors/400.ejs
Normal file
16
views/errors/400.ejs
Normal 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
16
views/errors/403.ejs
Normal 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
16
views/errors/404.ejs
Normal 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
16
views/errors/500.ejs
Normal 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
16
views/errors/503.ejs
Normal 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
9
views/partials/head.ejs
Normal 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>
|
8
views/partials/header.ejs
Normal file
8
views/partials/header.ejs
Normal 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>
|
@ -3,10 +3,15 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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/header.css">
|
||||
</head>
|
||||
<%- include("partials/head") %>
|
||||
<body>
|
||||
<%- include("partials/header") %>
|
||||
<div class="header-spacer"></div>
|
||||
<div class="main-content">
|
||||
<div class="content">
|
||||
<div class="schedule-container">
|
||||
<% Object.keys(groupedSchedules).forEach(date => { %>
|
||||
<div class="date-header">
|
||||
@ -30,5 +35,7 @@
|
||||
</div>
|
||||
<% }) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
46
views/terms.ejs
Normal file
46
views/terms.ejs
Normal 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>
|
Loading…
Reference in New Issue
Block a user