9 Commits

Author SHA1 Message Date
tuna2134
1f0c8a3333 fix 2026-05-27 12:33:51 +09:00
tuna2134
0784d78f8c add 2026-05-27 12:32:56 +09:00
tuna2134
7b05257b7d aaa 2026-05-27 12:31:24 +09:00
tuna2134
ab1611b6ea fix 2026-05-27 12:30:33 +09:00
tuna2134
a09f9aa6dc fix 2026-05-27 12:26:37 +09:00
tuna2134
4a754c8b87 fix 2026-05-27 12:25:29 +09:00
tuna2134
6c9d788095 fix 2026-05-27 12:20:05 +09:00
tuna2134
e08b5c475c fixed 2026-05-27 08:08:39 +09:00
tuna2134
efaa624b51 fixed 2026-05-27 08:07:11 +09:00
11 changed files with 173 additions and 21 deletions

30
.github/workflows/ghcr.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Push to github container register
on:
push:
branches: [main]
workflow_dispatch:
jobs:
push-docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: git.neody.ad.jp
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push image
uses: docker/build-push-action@v7
with:
context: .
push: true
tags: |
git.neody.ad.jp/tuna2134/cecilia:latest

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["bradlc.vscode-tailwindcss"]
}

27
Dockerfile Normal file
View File

@@ -0,0 +1,27 @@
FROM node:24-slim AS builder
WORKDIR /src
ENV OUTPUT=standalone
RUN corepack enable
COPY pnpm-lock.yaml package.json .
RUN pnpm install --frozen-lockfile --ignore-scripts
RUN pnpm approve-builds sharp
RUN pnpm rebuild sharp
COPY . .
RUN pnpm build
FROM gcr.io/distroless/nodejs24-debian12
# ENV NODE_ENV=production
WORKDIR /usr/src
COPY --chown=nonroot:nonroot ./public ./public
COPY --from=builder --chown=nonroot:nonroot /src/.next/standalone ./
COPY --from=builder --chown=nonroot:nonroot /src/.next/static ./.next/static
USER nonroot
EXPOSE 3000
CMD ["server.js"]

View File

@@ -5,4 +5,8 @@ const nextConfig: NextConfig = {
reactCompiler: true, reactCompiler: true,
}; };
if (process.env.OUTPUT === "standalone") {
nextConfig.output = "standalone";
}
export default nextConfig; export default nextConfig;

View File

@@ -10,6 +10,7 @@
"format": "biome format --write" "format": "biome format --write"
}, },
"dependencies": { "dependencies": {
"iconoir-react": "^7.11.0",
"next": "16.2.6", "next": "16.2.6",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4" "react-dom": "19.2.4"

12
pnpm-lock.yaml generated
View File

@@ -8,6 +8,9 @@ importers:
.: .:
dependencies: dependencies:
iconoir-react:
specifier: ^7.11.0
version: 7.11.0(react@19.2.4)
next: next:
specifier: 16.2.6 specifier: 16.2.6
version: 16.2.6(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) version: 16.2.6(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -479,6 +482,11 @@ packages:
graceful-fs@4.2.11: graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
iconoir-react@7.11.0:
resolution: {integrity: sha512-uvTKtnHYwbbTsmQ6HCcliYd50WK0GbjP497RwdISxKzfS01x4cK1Mn/F2mT/t2roSaJQ0I+KnHxMcyvmNMXWsQ==}
peerDependencies:
react: 18 || 19
jiti@2.7.0: jiti@2.7.0:
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true hasBin: true
@@ -954,6 +962,10 @@ snapshots:
graceful-fs@4.2.11: {} graceful-fs@4.2.11: {}
iconoir-react@7.11.0(react@19.2.4):
dependencies:
react: 19.2.4
jiti@2.7.0: {} jiti@2.7.0: {}
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.32.0:

View File

@@ -1,26 +1,41 @@
import { NextPage } from "next"; import { NextPage, Metadata } from "next";
import Image from "next/image"; import { Activity, ActivityData } from "@/components/pages/Home/Activity";
import { Profile } from "@/components/pages/Home/Profile";
export const metadata: Metadata = {
title: "ホーム",
description: "tuna2134のホームページ",
};
const activities: ActivityData[] = [
{
title: "cecilia",
description: "Next.jsで作成したポートフォリオサイトです。",
url: "https://github.com/tuna2134/cecilia",
},
{
title: "Neody Networks",
description: "AS152873をはじめインフラをメインで運用しています。",
url: "https://neody.ad.jp",
},
{
title: "EtherIP",
description: "RustでRFC3378であるEtherIPを実装しました。",
url: "https://github.com/tuna2134/EtherIP",
},
];
const Home: NextPage = () => { const Home: NextPage = () => {
return ( return (
<> <>
<div className="w-full flex items-center gap-4 py-8"> <Profile />
<Image
src="/images/icon.png"
alt="icon"
width={128}
height={128}
className="rounded-full"
/>
<div>
<h2 className="text-2xl font-bold"></h2>
<p>
tuna2134です
</p>
</div>
</div>
<div> <div>
<h3 className="text-xl font-bold mb-4"></h3> <h3 className="text-xl font-bold mb-4"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{activities.map((activity, index) => (
<Activity key={index} activity={activity} />
))}
</div>
</div> </div>
</> </>
); );

View File

@@ -27,9 +27,7 @@ export default function RootLayout({
lang="ja" lang="ja"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`} className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
> >
<body className="min-h-full flex flex-col bg-black text-white"> <body className="min-h-full flex flex-col">{children}</body>
{children}
</body>
</html> </html>
); );
} }

View File

@@ -2,7 +2,7 @@ const Header: React.FC = () => {
return ( return (
<header className="w-full flex flex-col items-center gap-1 py-4 border-b border-gray-400"> <header className="w-full flex flex-col items-center gap-1 py-4 border-b border-gray-400">
<h1 className="text-4xl font-bold">tuna2134</h1> <h1 className="text-4xl font-bold">tuna2134</h1>
<small className="text-sm text-gray-400/90"> <small className="text-sm text-gray-600/80">
tuna2134の個人的な憩いの場 tuna2134の個人的な憩いの場
</small> </small>
</header> </header>

View File

@@ -0,0 +1,28 @@
import Link from "next/link";
export interface ActivityData {
title: string;
description: string;
url: string | null;
}
interface ActivityProps {
activity: ActivityData;
}
export const Activity: React.FC<ActivityProps> = ({ activity }) => {
return (
<div className="p-4 border border-gray-300 rounded-lg mb-4 flex flex-col">
<h4 className="text-lg font-semibold">{activity.title}</h4>
<p className="grow mb-2 text-gray-700">{activity.description}</p>
{activity.url && (
<Link
href={activity.url}
className="text-blue-500 hover:underline text-sm"
>
</Link>
)}
</div>
);
};

View File

@@ -0,0 +1,34 @@
import { Github, Mail, Twitter } from "iconoir-react";
import Image from "next/image";
import Link from "next/link";
export const Profile: React.FC = () => {
return (
<div className="w-full flex items-center gap-4 py-8">
<Image
src="/images/icon.png"
alt="icon"
width={128}
height={128}
className="rounded-full"
/>
<div>
<h2 className="text-2xl font-bold"></h2>
<p>
tuna2134です
</p>
<div className="mt-4 flex items-center gap-2">
<Link href="https://x.com/fdc_tuna2134">
<Twitter />
</Link>
<Link href="https://github.com/tuna2134">
<Github />
</Link>
<Link href="mailto:m-kikuchi@tuna2134.dev">
<Mail />
</Link>
</div>
</div>
</div>
);
};