Add Dockerfile, nginx config, Gitea workflow and docker-compose
Some checks failed
Build and Publish Docker Image / build (push) Has been cancelled

This commit is contained in:
2026-02-15 20:14:58 +01:00
parent 91203e6c2c
commit 9272bd9c7d
11 changed files with 321 additions and 111 deletions

View File

@@ -0,0 +1,30 @@
name: Build and Publish Docker Image
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Gitea Registry
uses: docker/login-action@v2
with:
registry: gitea.7u.pl
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: gitea.7u.pl/gkucmierz/rubic-cube:latest

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Production stage
FROM nginx:stable-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

15
docker-compose.yml Normal file
View File

@@ -0,0 +1,15 @@
version: '3.8'
services:
rubic-cube:
image: gitea.7u.pl/gkucmierz/rubic-cube:latest
container_name: rubic-cube
restart: always
ports:
- "8083:80"
networks:
- rubic-net
networks:
rubic-net:
driver: bridge

View File

@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/src/assets/rubic-cube.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Rubic Cube</title> <title>Rubic Cube</title>
</head> </head>

15
nginx.conf Normal file
View File

@@ -0,0 +1,15 @@
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

View File

@@ -1,9 +1,9 @@
<script setup> <script setup>
import HelloWorld from './components/HelloWorld.vue' import main from './components/main.vue'
</script> </script>
<template> <template>
<HelloWorld /> <Main />
</template> </template>
<style scoped> <style scoped>

55
src/assets/rubic-cube.svg Normal file
View File

@@ -0,0 +1,55 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<style>
.face { stroke: #000; stroke-width: 6; stroke-linejoin: round; }
.top { fill: #ffffff; }
.left { fill: #009e60; }
.right { fill: #c41e3a; }
</style>
</defs>
<!-- Top Face -->
<g class="face top">
<path d="M256 80 L309.33 106.67 L256 133.33 L202.67 106.67 Z" />
<path d="M309.33 106.67 L362.67 133.33 L309.33 160 L256 133.33 Z" />
<path d="M362.67 133.33 L416 160 L362.67 186.67 L309.33 160 Z" />
<path d="M202.67 106.67 L256 133.33 L202.67 160 L149.33 133.33 Z" />
<path d="M256 133.33 L309.33 160 L256 186.67 L202.67 160 Z" />
<path d="M309.33 160 L362.67 186.67 L309.33 213.33 L256 186.67 Z" />
<path d="M149.33 133.33 L202.67 160 L149.33 186.67 L96 160 Z" />
<path d="M202.67 160 L256 186.67 L202.67 213.33 L149.33 186.67 Z" />
<path d="M256 186.67 L309.33 213.33 L256 240 L202.67 213.33 Z" />
</g>
<!-- Left Face -->
<g class="face left">
<path d="M96 160 L149.33 186.67 L149.33 248 L96 221.33 Z" />
<path d="M149.33 186.67 L202.67 213.33 L202.67 274.67 L149.33 248 Z" />
<path d="M202.67 213.33 L256 240 L256 301.33 L202.67 274.67 Z" />
<path d="M96 221.33 L149.33 248 L149.33 309.33 L96 282.67 Z" />
<path d="M149.33 248 L202.67 274.67 L202.67 336 L149.33 309.33 Z" />
<path d="M202.67 274.67 L256 301.33 L256 362.67 L202.67 336 Z" />
<path d="M96 282.67 L149.33 309.33 L149.33 370.67 L96 344 Z" />
<path d="M149.33 309.33 L202.67 336 L202.67 397.33 L149.33 370.67 Z" />
<path d="M202.67 336 L256 362.67 L256 424 L202.67 397.33 Z" />
</g>
<!-- Right Face -->
<g class="face right">
<path d="M256 240 L309.33 213.33 L309.33 274.67 L256 301.33 Z" />
<path d="M309.33 213.33 L362.67 186.67 L362.67 248 L309.33 274.67 Z" />
<path d="M362.67 186.67 L416 160 L416 221.33 L362.67 248 Z" />
<path d="M256 301.33 L309.33 274.67 L309.33 336 L256 362.67 Z" />
<path d="M309.33 274.67 L362.67 248 L362.67 309.33 L309.33 336 Z" />
<path d="M362.67 248 L416 221.33 L416 282.67 L362.67 309.33 Z" />
<path d="M256 362.67 L309.33 336 L309.33 397.33 L256 424 Z" />
<path d="M309.33 336 L362.67 309.33 L362.67 370.67 L309.33 397.33 Z" />
<path d="M362.67 309.33 L416 282.67 L416 344 L362.67 370.67 Z" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

Before

Width:  |  Height:  |  Size: 496 B

View File

@@ -1,107 +0,0 @@
<script setup>
</script>
<template>
<div class="container">
<div class="cube">
<div class="face top">Top</div>
<div class="face bottom">Bottom</div>
<div class="face left">Left</div>
<div class="face right">Right</div>
<div class="face front">Front</div>
<div class="face back">Back</div>
</div>
</div>
</template>
<style scoped>
.container {
width: 200px;
height: 200px;
perspective: 400px;
margin: 100px;
}
.top {
background: white;
}
.bottom {
background: yellow;
}
.front {
background: blue;
}
.back {
background: green;
}
.left {
background: red;
}
.right {
background: orange;
}
.cube {
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d;
}
.face {
width: 200px;
height: 200px;
border: 0px solid black;
position: absolute;
opacity: 0.9;
display: flex;
align-items: center;
justify-content: center;
font-family: Arial, sans-serif;
font-size: 2rem;
}
.cube {
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d;
transform: rotate3d(1, 1, 0, 45deg);
}
.front {
transform: translateZ(100px);
}
.back {
transform: translateZ(-100px) rotateY(180deg);
}
.left {
transform: translateX(-100px) rotateY(-90deg);
}
.right {
transform: translateX(100px) rotateY(90deg);
}
.top {
transform: translateY(-100px) rotateX(90deg);
}
.bottom {
transform: translateY(100px) rotateX(-90deg);
}
@keyframes turn {
from { transform: rotate3d(0, 0, 0, 0); }
to { transform: rotate3d(1, 1, 0, 360deg); }
}
.cube {
position: relative;
transform-style: preserve-3d;
animation: turn 3s linear infinite;
}
</style>

174
src/components/main.vue Normal file
View File

@@ -0,0 +1,174 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
const rx = ref(25)
const ry = ref(25)
const rz = ref(0)
const isDragging = ref(false)
const lastMouseX = ref(0)
const lastMouseY = ref(0)
const onMouseDown = (event) => {
isDragging.value = true
lastMouseX.value = event.clientX
lastMouseY.value = event.clientY
}
const onMouseMove = (event) => {
if (!isDragging.value) return
const deltaX = event.clientX - lastMouseX.value
const deltaY = event.clientY - lastMouseY.value
ry.value += deltaX * 0.5
rx.value -= deltaY * 0.5
lastMouseX.value = event.clientX
lastMouseY.value = event.clientY
}
const onMouseUp = () => {
isDragging.value = false
}
onMounted(() => {
window.addEventListener('mousemove', onMouseMove)
window.addEventListener('mouseup', onMouseUp)
})
onUnmounted(() => {
window.removeEventListener('mousemove', onMouseMove)
window.removeEventListener('mouseup', onMouseUp)
})
const cubeStyle = computed(() => ({
transform: `rotateX(${rx.value}deg) rotateY(${ry.value}deg) rotateZ(${rz.value}deg)`
}))
</script>
<template>
<div class="wrapper">
<div class="container" @mousedown="onMouseDown">
<div class="cube" :style="cubeStyle">
<div class="face top">
<div class="stickers">
<div class="sticker" v-for="i in 9" :key="'t'+i"></div>
</div>
</div>
<div class="face bottom">
<div class="stickers">
<div class="sticker" v-for="i in 9" :key="'b'+i"></div>
</div>
</div>
<div class="face left">
<div class="stickers">
<div class="sticker" v-for="i in 9" :key="'l'+i"></div>
</div>
</div>
<div class="face right">
<div class="stickers">
<div class="sticker" v-for="i in 9" :key="'r'+i"></div>
</div>
</div>
<div class="face front">
<div class="stickers">
<div class="sticker" v-for="i in 9" :key="'f'+i"></div>
</div>
</div>
<div class="face back">
<div class="stickers">
<div class="sticker" v-for="i in 9" :key="'k'+i"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
}
.container {
width: 200px;
height: 200px;
perspective: 600px;
margin: 60px auto;
cursor: grab;
user-select: none;
}
.container:active {
cursor: grabbing;
}
.cube {
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d;
}
.face {
width: 200px;
height: 200px;
background: #000;
padding: 6px;
box-sizing: border-box;
position: absolute;
opacity: 0.9;
}
.stickers {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
gap: 6px;
width: 100%;
height: 100%;
}
.sticker {
width: 100%;
height: 100%;
background: var(--sticker-color);
border-radius: 6px;
}
.top { --sticker-color: #ffffff; }
.bottom { --sticker-color: #ffd500; }
.front { --sticker-color: #0051ba; }
.back { --sticker-color: #009e60; }
.left { --sticker-color: #c41e3a; }
.right { --sticker-color: #ff5800; }
.front {
transform: translateZ(100px);
}
.back {
transform: translateZ(-100px) rotateY(180deg);
}
.left {
transform: translateX(-100px) rotateY(-90deg);
}
.right {
transform: translateX(100px) rotateY(90deg);
}
.top {
transform: translateY(-100px) rotateX(90deg);
}
.bottom {
transform: translateY(100px) rotateX(-90deg);
}
</style>

View File

@@ -4,4 +4,13 @@ import vue from '@vitejs/plugin-vue'
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
server: {
port: 5174,
hmr: {
overlay: true,
},
watch: {
usePolling: true,
},
},
}) })