27 Commits
0.0.7 ... main

Author SHA1 Message Date
9198095c28 gitea to git
All checks were successful
continuous-integration/drone/tag Build is passing
2025-10-21 23:55:37 +05:30
71177a50e6 fixes for IMAGE_TAG
All checks were successful
continuous-integration/drone/tag Build is passing
2025-10-21 19:31:47 +05:30
887e3835c2 Update .drone.yml 2025-10-21 13:58:09 +00:00
c4f51a59c8 do not trust AI to write good code
All checks were successful
continuous-integration/drone Build is passing
2025-10-21 13:53:12 +00:00
40f4fabfe2 do not trust AI to write good code
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 13:51:21 +00:00
23bce67595 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 13:50:13 +00:00
49d1e241c3 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 13:46:55 +00:00
438bf18325 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 13:40:16 +00:00
b69af60f87 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 13:37:20 +00:00
b69fca13cb Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 12:47:44 +00:00
95e58cef54 temporary removed check and build stage to force push image to registry
Some checks reported errors
continuous-integration/drone Build was killed
2025-10-21 12:39:28 +00:00
edf9c08848 Update .drone.yml
All checks were successful
continuous-integration/drone Build is passing
2025-10-21 12:38:09 +00:00
a7ba9c2ea8 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 12:28:09 +00:00
cd9ed14475 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 12:25:20 +00:00
f80ffa058e Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 12:23:43 +00:00
4cc35f9422 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 12:21:11 +00:00
58da548058 Update .drone.yml
Some checks failed
continuous-integration/drone Build is failing
2025-10-21 12:12:14 +00:00
1d1b71d780 Update .drone.yml 2025-10-21 12:11:20 +00:00
f69de26c1b added Netdata in monitoring services
Some checks failed
continuous-integration/drone/tag Build is failing
2025-10-12 15:31:25 +05:30
0431998723 added step in .drone.yml to push image to REGISTRY_HOST 2025-10-12 15:01:46 +05:30
69a9e8000c changed http to https
All checks were successful
continuous-integration/drone/tag Build is passing
2025-10-11 22:34:32 +05:30
6afbc899e1 always restart docker container 2025-10-11 22:29:25 +05:30
fde6be3b18 only run drone on pushing tag 2025-10-11 22:25:01 +05:30
d27b793cd4 scroll in ServiceList 2025-10-11 22:19:18 +05:30
2cdee4a028 fixed maxHeight for ServiceList parent Card as 50vh 2025-10-11 22:17:49 +05:30
081e3bf2b7 removed stray code 2025-10-11 22:15:25 +05:30
e4f40811b6 ident fixes and config 2025-10-11 15:23:51 +05:30
5 changed files with 250 additions and 169 deletions

View File

@@ -3,7 +3,6 @@ kind: pipeline
type: docker type: docker
name: default name: default
# ARM64 platform (for your Pi runner)
platform: platform:
os: linux os: linux
arch: arm64 arch: arm64
@@ -11,80 +10,121 @@ platform:
workspace: workspace:
path: /drone/src path: /drone/src
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
steps: steps:
# Step 1a: Git fetch tags locally
- name: fetch-tags - name: fetch-tags
image: docker:24 image: docker:24
environment: volumes:
DOCKER_HOST: unix:///var/run/docker.sock # local Docker, if needed - name: dockersock
path: /var/run/docker.sock
commands: commands:
- apk add --no-cache git - apk add --no-cache git
- git fetch --tags - git fetch --tags
- | - |
LATEST_TAG=$(git describe --tags --abbrev=0) # Get latest Git tag and trim newline
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null | tr -d '\n')
echo "Latest Git tag fetched: $LATEST_TAG" echo "Latest Git tag fetched: $LATEST_TAG"
# Save to file to share with next step
echo $LATEST_TAG > /drone/src/LATEST_TAG.txt
# Step 1b: Check if image exists on remote LAN Docker # Save to file for downstream steps
- name: check-remote-image echo "$LATEST_TAG" > /drone/src/LATEST_TAG.txt
image: docker:24
environment:
DOCKER_HOST: tcp://192.168.1.111:2376 # remote Docker
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt)
- echo "Checking if apps/homepage:$IMAGE_TAG exists on remote Docker..."
- |
if docker image inspect apps/homepage:$IMAGE_TAG > /dev/null 2>&1; then
echo "✅ Docker image apps/homepage:$IMAGE_TAG already exists — skipping build";
exit 78; # stop pipeline gracefully
else
echo "⚙️ Docker image apps/homepage:$IMAGE_TAG not found — proceeding to build...";
fi
# Step 2: Build Docker image (dynamic tag) # Read back for verification
- name: build-image IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
image: docker:24 echo "Image tag read from file: $IMAGE_TAG"
environment:
DOCKER_HOST: tcp://192.168.1.111:2376 # Validate
commands:
- |
IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt)
if [ -z "$IMAGE_TAG" ]; then if [ -z "$IMAGE_TAG" ]; then
echo "❌ No tag found in LATEST_TAG.txt — cannot build." echo "❌ No git tags found! Cannot continue."
exit 1 exit 1
fi fi
echo "🔨 Building Docker image apps/homepage:$IMAGE_TAG ..."
docker build --network=host -t apps/homepage:$IMAGE_TAG -t apps/homepage:latest .
# Step 3: Stop old container (if exists) - name: check-remote-image
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "Checking if apps/homepage:$IMAGE_TAG exists on remote Docker..."
- echo "Existing Docker tags for apps/homepage:"
- docker images --format "{{.Repository}}:{{.Tag}}" | grep "^apps/homepage" || echo "(none)"
- |
if docker image inspect apps/homepage:$IMAGE_TAG > /dev/null 2>&1; then
echo "✅ Docker image apps/homepage:$IMAGE_TAG already exists — skipping build"
exit 78
else
echo "⚙️ Docker image apps/homepage:$IMAGE_TAG not found — proceeding to build..."
fi
- name: build-image
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "🔨 Building Docker image apps/homepage:$IMAGE_TAG ..."
- docker build --network=host -t apps/homepage:$IMAGE_TAG -t apps/homepage:latest /drone/src
- name: push-image
image: docker:24
environment:
REGISTRY_HOST:
from_secret: REGISTRY_HOST
REGISTRY_USER:
from_secret: REGISTRY_USER
REGISTRY_PASS:
from_secret: REGISTRY_PASS
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "🔑 Logging into registry $REGISTRY_HOST ..."
- echo "$REGISTRY_PASS" | docker login $REGISTRY_HOST -u "$REGISTRY_USER" --password-stdin
- echo "🏷️ Tagging images with registry prefix..."
- docker tag apps/homepage:$IMAGE_TAG $REGISTRY_HOST/apps/homepage:$IMAGE_TAG
- docker tag apps/homepage:$IMAGE_TAG $REGISTRY_HOST/apps/homepage:latest
- echo "📤 Pushing apps/homepage:$IMAGE_TAG ..."
- docker push $REGISTRY_HOST/apps/homepage:$IMAGE_TAG
- echo "📤 Pushing apps/homepage:latest ..."
- docker push $REGISTRY_HOST/apps/homepage:latest
- name: stop-old - name: stop-old
image: docker:24 image: docker:24
environment: volumes:
DOCKER_HOST: tcp://192.168.1.111:2376 - name: dockersock
path: /var/run/docker.sock
commands: commands:
- | - echo "🛑 Stopping old container..."
echo "🛑 Stopping old container..." - docker rm -f homepage || true
docker rm -f homepage || true
# Step 4: Run container
- name: run-container - name: run-container
image: docker:24 image: docker:24
environment: volumes:
DOCKER_HOST: tcp://192.168.1.111:2376 - name: dockersock
path: /var/run/docker.sock
commands: commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "🚀 Starting container apps/homepage:$IMAGE_TAG ..."
- | - |
IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt)
echo "🚀 Starting container apps/homepage:$IMAGE_TAG ..."
docker run -d \ docker run -d \
--name homepage \ --name homepage \
-p 3001:3000 \ -p 3001:3000 \
-e NODE_ENV=production \ -e NODE_ENV=production \
--restart always \
apps/homepage:$IMAGE_TAG apps/homepage:$IMAGE_TAG
# Trigger rules # Trigger rules
trigger: trigger:
event: event:
- push
- tag - tag
- custom - custom

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@@ -1,41 +1,49 @@
import React from 'react'; import React from 'react';
import Box from '@mui/material/Box';
import {Grid, Paper, Link, Typography} from '@mui/material'; import {Grid, Paper, Link, Typography} from '@mui/material';
interface ServiceList { interface ServiceList {
name: string; name: string;
url: string; url: string;
desc: string; desc: string;
external?: boolean; external?: boolean;
} }
interface ServiceListProps { interface ServiceListProps {
serviceList: ServiceList[]; serviceList: ServiceList[];
} }
const ServiceList: React.FC<ServiceListProps> = ({serviceList}) => { const ServiceList: React.FC<ServiceListProps> = ({serviceList}) => {
return ( return (
<Grid container spacing={3} justifyContent="center"> <Box
{serviceList.map((s) => ( sx={{
<Paper flex: 1,
elevation={3} overflowY: 'auto', // ✅ Scroll only inside this
sx={{p: 2, textAlign: "center", bgcolor: "#2d2d2d", borderRadius: 2}} }}
> >
<Link <Grid container spacing={3} justifyContent="center">
href={s.url} {serviceList.map((s) => (
target={s.external ? "_blank" : undefined} <Paper
rel="noopener" elevation={3}
underline="none" sx={{p: 2, textAlign: "center", bgcolor: "#2d2d2d", borderRadius: 2}}
sx={{fontSize: 18, fontWeight: "bold", color: "success.main"}} >
> <Link
{s.name} href={s.url}
</Link> target={s.external ? "_blank" : undefined}
<Typography variant="body2" sx={{mt: 1, color: "#ccc"}}> rel="noopener"
{s.desc} underline="none"
</Typography> sx={{fontSize: 18, fontWeight: "bold", color: "success.main"}}
</Paper> >
))} {s.name}
</Grid> </Link>
); <Typography variant="body2" sx={{mt: 1, color: "#ccc"}}>
{s.desc}
</Typography>
</Paper>
))}
</Grid>
</Box>
);
}; };
export default ServiceList; export default ServiceList;

View File

@@ -5,7 +5,7 @@ import Card from '@mui/material/Card';
import MuiChip from '@mui/material/Chip'; import MuiChip from '@mui/material/Chip';
import Container from '@mui/material/Container'; import Container from '@mui/material/Container';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { styled } from '@mui/material/styles'; import {styled} from '@mui/material/styles';
import PermMediaIcon from '@mui/icons-material/PermMedia'; import PermMediaIcon from '@mui/icons-material/PermMedia';
import CodeIcon from '@mui/icons-material/Code'; import CodeIcon from '@mui/icons-material/Code';
@@ -15,33 +15,79 @@ import ServiceList from "~/components/ServiceList";
const items = [ const items = [
{ {
icon: <PermMediaIcon />, icon: <PermMediaIcon/>,
title: 'The Vox Sanctum', title: 'The Vox Sanctum',
serviceList: [ serviceList: [
{ name: "Jellyseerr", url: "http://jellyseerr.aetoskia.com", desc: "Summon films and series from the digital void.", external: true }, {
{ name: "Sonarr", url: "http://sonarr.aetoskia.com", desc: "Keep the endless chronicles of TV under iron control.", external: true }, name: "Jellyseerr",
{ name: "Radarr", url: "http://radarr.aetoskia.com", desc: "Command the legions of cinema, enforce cinematic order.", external: true }, url: "https://jellyseerr.aetoskia.com",
desc: "Summon films and series from the digital void.",
external: true
},
{
name: "Sonarr",
url: "https://sonarr.aetoskia.com",
desc: "Keep the endless chronicles of TV under iron control.",
external: true
},
{
name: "Radarr",
url: "https://radarr.aetoskia.com",
desc: "Command the legions of cinema, enforce cinematic order.",
external: true
},
], ],
description: description:
"Behold the archive of visual legends, where the eternal campaigns of film and series march forth in eternal crusade against chaos and forgetfulness.", "Behold the archive of visual legends, where the eternal campaigns of film and series march forth in eternal crusade against chaos and forgetfulness.",
}, },
{ {
icon: <CodeIcon />, icon: <CodeIcon/>,
title: 'The Forge Conclave', title: 'The Forge Conclave',
serviceList: [ serviceList: [
{ name: "Gitea", url: "http://gitea.aetoskia.com", desc: "Forge and safeguard code like a sacred relic.", external: true }, {
{ name: "Registry", url: "http://registry.aetoskia.com", desc: "Monitor core constructs of the digital empire.", external: true }, name: "Git",
{ name: "Drone", url: "http://drone.aetoskia.com", desc: "Automaton architect, building pipelines of perfection.", external: true }, url: "https://git.aetoskia.com",
desc: "Forge and safeguard code like a sacred relic.",
external: true
},
{
name: "Registry",
url: "https://registry.aetoskia.com",
desc: "Monitor core constructs of the digital empire.",
external: true
},
{
name: "Drone",
url: "https://drone.aetoskia.com",
desc: "Automaton architect, building pipelines of perfection.",
external: true
},
], ],
description: description:
"The bastion of creation — where code is forged in the fires of discipline, guarded like relics, and deployed with unyielding precision to uphold the empire's might.", "The bastion of creation — where code is forged in the fires of discipline, guarded like relics, and deployed with unyielding precision to uphold the empire's might.",
}, },
{ {
icon: <MonitorHeartIcon />, icon: <MonitorHeartIcon/>,
title: 'The Vigilant Watch', title: 'The Vigilant Watch',
serviceList: [ serviceList: [
{ name: "Portainer", url: "http://portainer.aetoskia.com", desc: "Oversee the fleet of containers with unyielding vigilance.", external: true }, {
{ name: "Traefik", url: "http://traefik.aetoskia.com", desc: "Marshal your gateways and protect the flow between realms.", external: true }, name: "Netdata",
url: "https://netdata.aetoskia.com",
desc: "Watch over the mechanized legions and digital armories with the unblinking eye of the Omnissiah.",
external: true
},
{
name: "Portainer",
url: "https://portainer.aetoskia.com",
desc: "Oversee the fleet of containers with unyielding vigilance.",
external: true
},
{
name: "Traefik",
url: "https://traefik.aetoskia.com",
desc: "Marshal your gateways and protect the flow between realms.",
external: true
},
], ],
description: description:
"Eyes ever watchful, guarding the realms sanctity — these sentinels oversee the flow of life and command the paths between digital dominions.", "Eyes ever watchful, guarding the realms sanctity — these sentinels oversee the flow of life and command the paths between digital dominions.",
@@ -52,10 +98,10 @@ interface ChipProps {
selected?: boolean; selected?: boolean;
} }
const Chip = styled(MuiChip)<ChipProps>(({ theme }) => ({ const Chip = styled(MuiChip)<ChipProps>(({theme}) => ({
variants: [ variants: [
{ {
props: ({ selected }) => !!selected, props: ({selected}) => !!selected,
style: { style: {
background: background:
'linear-gradient(to bottom right, hsl(210, 98%, 48%), hsl(210, 98%, 35%))', 'linear-gradient(to bottom right, hsl(210, 98%, 48%), hsl(210, 98%, 35%))',
@@ -79,10 +125,10 @@ interface MobileLayoutProps {
} }
export function MobileLayout({ export function MobileLayout({
selectedItemIndex, selectedItemIndex,
handleItemClick, handleItemClick,
selectedFeature, selectedFeature,
}: MobileLayoutProps) { }: MobileLayoutProps) {
if (!items[selectedItemIndex]) { if (!items[selectedItemIndex]) {
return null; return null;
} }
@@ -90,13 +136,13 @@ export function MobileLayout({
return ( return (
<Box <Box
sx={{ sx={{
display: { xs: 'flex', sm: 'none' }, display: {xs: 'flex', sm: 'none'},
flexDirection: 'column', flexDirection: 'column',
gap: 2, gap: 2,
}} }}
> >
<Box sx={{ display: 'flex', gap: 2, overflow: 'auto' }}> <Box sx={{display: 'flex', gap: 2, overflow: 'auto'}}>
{items.map(({ title }, index) => ( {items.map(({title}, index) => (
<Chip <Chip
size="medium" size="medium"
key={index} key={index}
@@ -107,14 +153,14 @@ export function MobileLayout({
))} ))}
</Box> </Box>
<Card variant="outlined"> <Card variant="outlined">
<Box sx={{ px: 2, pb: 2 }}> <Box sx={{px: 2, pb: 2}}>
<Typography <Typography
gutterBottom gutterBottom
sx={{ color: 'text.primary', fontWeight: 'medium' }} sx={{color: 'text.primary', fontWeight: 'medium'}}
> >
{selectedFeature.title} {selectedFeature.title}
</Typography> </Typography>
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 1.5 }}> <Typography variant="body2" sx={{color: 'text.secondary', mb: 1.5}}>
{selectedFeature.description} {selectedFeature.description}
</Typography> </Typography>
</Box> </Box>
@@ -134,84 +180,83 @@ export default function Services() {
const selectedFeature = items[selectedItemIndex]; const selectedFeature = items[selectedItemIndex];
return ( return (
<Container id="features" sx={{ bgcolor: "#1a1a1a", borderRadius: 3, p: 3, boxShadow: 6 }}> <Container id="features" sx={{bgcolor: "#1a1a1a", borderRadius: 3, p: 3, boxShadow: 6}}>
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexDirection: { xs: 'column', md: 'row-reverse' }, flexDirection: {xs: 'column', md: 'row-reverse'},
gap: 2, gap: 2,
}} }}
> >
<div> <Box
<Box sx={{
sx={{ display: {xs: 'none', sm: 'flex'},
display: { xs: 'none', sm: 'flex' }, flexDirection: 'column',
flexDirection: 'column', gap: 2,
gap: 2, height: '100%',
height: '100%', }}
}} >
> {items.map(({icon, title, description}, index) => (
{items.map(({ icon, title, description }, index) => ( <Box
key={index}
component={Button}
onClick={() => handleItemClick(index)}
sx={[
(theme) => ({
p: 2,
height: '100%',
width: '100%',
'&:hover': {
backgroundColor: (theme.vars || theme).palette.action.hover,
},
}),
selectedItemIndex === index && {
backgroundColor: 'action.selected',
},
]}
>
<Box <Box
key={index}
component={Button}
onClick={() => handleItemClick(index)}
sx={[ sx={[
(theme) => ({ {
p: 2,
height: '100%',
width: '100%', width: '100%',
'&:hover': { display: 'flex',
backgroundColor: (theme.vars || theme).palette.action.hover, flexDirection: 'column',
}, alignItems: 'left',
}), gap: 1,
textAlign: 'left',
textTransform: 'none',
color: 'text.secondary',
},
selectedItemIndex === index && { selectedItemIndex === index && {
backgroundColor: 'action.selected', color: 'text.primary',
}, },
]} ]}
> >
<Box <Typography variant="h6">{icon} {title}</Typography>
sx={[ <Typography variant="body2">{description}</Typography>
{
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'left',
gap: 1,
textAlign: 'left',
textTransform: 'none',
color: 'text.secondary',
},
selectedItemIndex === index && {
color: 'text.primary',
},
]}
>
<Typography variant="h6">{icon} {title}</Typography>
<Typography variant="body2">{description}</Typography>
</Box>
</Box> </Box>
))} </Box>
</Box> ))}
<MobileLayout </Box>
selectedItemIndex={selectedItemIndex} <MobileLayout
handleItemClick={handleItemClick} selectedItemIndex={selectedItemIndex}
selectedFeature={selectedFeature} handleItemClick={handleItemClick}
/> selectedFeature={selectedFeature}
</div> />
<Box <Box
sx={{ sx={{
display: { xs: 'none', sm: 'flex' }, display: {xs: 'none', sm: 'flex'},
width: { xs: '100%', md: '70%' }, width: {xs: '100%', md: '70%'},
}} }}
> >
<Card <Card
variant="outlined" variant="outlined"
sx={{ sx={{
display: { xs: 'none', sm: 'flex' }, display: {xs: 'none', sm: 'flex'},
maxHeight: '50vh',
}} }}
> >
<ServiceList serviceList={selectedFeature.serviceList} /> <ServiceList serviceList={selectedFeature.serviceList} />
</Card> </Card>
</Box> </Box>
</Box> </Box>

View File

@@ -13,23 +13,6 @@ export function meta() {
]; ];
} }
const services = {
media: [
{ name: "Jellyseerr", url: "http://jellyseerr.aetoskia.com", desc: "Summon films and series from the digital void.", external: true },
{ name: "Sonarr", url: "http://sonarr.aetoskia.com", desc: "Keep the endless chronicles of TV under iron control.", external: true },
{ name: "Radarr", url: "http://radarr.aetoskia.com", desc: "Command the legions of cinema, enforce cinematic order.", external: true },
{ name: "qBit", url: "http://qbit.aetoskia.com", desc: "Torrent war engine, fetching data across the nether realms.", external: true },
],
codebase: [
{ name: "Gitea", url: "http://gitea.aetoskia.com", desc: "Forge and safeguard code like a sacred relic.", external: true },
{ name: "Registry", url: "http://registry.aetoskia.com", desc: "Monitor core constructs of the digital empire.", external: true },
{ name: "Drone", url: "http://drone.aetoskia.com", desc: "Automaton architect, building pipelines of perfection.", external: true },
],
monitoring: [
{ name: "Portainer", url: "http://portainer.aetoskia.com", desc: "Oversee the fleet of containers with unyielding vigilance.", external: true },
],
};
export default function Home() { export default function Home() {
return ( return (
<Container <Container