deploy
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
# Base URL of the backend HTTP API.
|
# Base URL of the backend HTTP API.
|
||||||
VITE_API_BASE=https://localhost:3000
|
VITE_API_BASE=http://localhost:3000
|
||||||
|
|
||||||
# Base URL of the backend websocket. Optional: when omitted it is derived
|
# Base URL of the backend websocket. Optional: when omitted it is derived
|
||||||
# from VITE_API_BASE by swapping the protocol (https -> wss).
|
# from VITE_API_BASE by swapping the protocol (https -> wss).
|
||||||
VITE_WS_BASE=wss://localhost:3000
|
VITE_WS_BASE=ws://localhost:3000
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: Build and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
git clone ${{ github.server_url }}/${{ github.repository }}.git .
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Login to Gitea Registry
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
|
||||||
|
docker login https://gitea.johannesbot.de -u ${{ secrets.REGISTRY_USER }} --password-stdin
|
||||||
|
|
||||||
|
- name: Build Image
|
||||||
|
run: docker build -t gitea.johannesbot.de/johannesbot/mc-computer-craft-api-frontend:latest .
|
||||||
|
|
||||||
|
- name: Push Image
|
||||||
|
run: docker push gitea.johannesbot.de/johannesbot/mc-computer-craft-api-frontend:latest
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create deployment directory
|
||||||
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
username: ${{ secrets.SSH_USER }}
|
||||||
|
password: ${{ secrets.SSH_PASSWORD }}
|
||||||
|
port: ${{ secrets.SSH_PORT || 22 }}
|
||||||
|
script: mkdir -p /home/${{ secrets.SSH_USER }}/mc-computer-craft-api-frontend
|
||||||
|
|
||||||
|
- name: Deploy via SSH
|
||||||
|
uses: appleboy/ssh-action@v1.0.3
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.SSH_HOST }}
|
||||||
|
username: ${{ secrets.SSH_USER }}
|
||||||
|
password: ${{ secrets.SSH_PASSWORD }}
|
||||||
|
port: ${{ secrets.SSH_PORT || 22 }}
|
||||||
|
script: |
|
||||||
|
cd /home/${{ secrets.SSH_USER }}/mc-computer-craft-api
|
||||||
|
# Login as root/sudo to ensure we can pull
|
||||||
|
echo "${{ secrets.REGISTRY_PASSWORD }}" | sudo docker login https://gitea.johannesbot.de -u ${{ secrets.REGISTRY_USER }} --password-stdin
|
||||||
|
sudo docker compose pull
|
||||||
|
sudo docker compose up -d --remove-orphans
|
||||||
|
sudo docker image prune -f
|
||||||
|
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@ export interface LiveItem {
|
|||||||
mod: string;
|
mod: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
/** Optional icon URL served by the backend (e.g. "/api/icon/minecraft/iron_ingot.png"). */
|
||||||
|
icon?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModInfo {
|
export interface ModInfo {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ function onModChange(event: Event): void {
|
|||||||
function toggleOrder(): void {
|
function toggleOrder(): void {
|
||||||
emit('update', { order: props.filters.order === 'asc' ? 'desc' : 'asc' });
|
emit('update', { order: props.filters.order === 'asc' ? 'desc' : 'asc' });
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="filter-bar panel">
|
<div class="filter-bar panel">
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function buildConfig(): ChartConfiguration<'line'> {
|
|||||||
bodyColor: '#2fd4c4',
|
bodyColor: '#2fd4c4',
|
||||||
padding: 10,
|
padding: 10,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (ctx) => ` ${fullNumber(ctx.parsed.y)}`,
|
label: (ctx) => ` ${fullNumber(ctx.parsed.y ?? 0)}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import type { LiveItem } from '../api/types.js';
|
|
||||||
import { abbreviateNumber, fullNumber } from '../utils/format.js';
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
items: LiveItem[];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
function openItem(item: LiveItem): void {
|
|
||||||
router.push({ name: 'item', params: { name: item.name } });
|
|
||||||
}
|
|
||||||
|
|
||||||
function label(item: LiveItem): string {
|
|
||||||
return item.displayName ?? item.name;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="table-wrap panel">
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="table__col-item">Item</th>
|
|
||||||
<th class="table__col-mod">Mod</th>
|
|
||||||
<th class="table__col-amount">Amount</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="item in items"
|
|
||||||
:key="item.name"
|
|
||||||
class="table__row"
|
|
||||||
tabindex="0"
|
|
||||||
@click="openItem(item)"
|
|
||||||
@keydown.enter="openItem(item)"
|
|
||||||
>
|
|
||||||
<td class="table__col-item">
|
|
||||||
<span class="table__name">{{ label(item) }}</span>
|
|
||||||
<span class="table__id mono">{{ item.name }}</span>
|
|
||||||
</td>
|
|
||||||
<td class="table__col-mod">
|
|
||||||
<span class="tag mono">{{ item.mod }}</span>
|
|
||||||
</td>
|
|
||||||
<td class="table__col-amount mono" :title="fullNumber(item.amount)">
|
|
||||||
{{ abbreviateNumber(item.amount) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="items.length === 0">
|
|
||||||
<td colspan="3" class="table__empty">No items match the current filters.</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.table-wrap {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th {
|
|
||||||
text-align: left;
|
|
||||||
font-size: 11px;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: var(--text-dim);
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
background: var(--bg-panel-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__col-amount {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__row {
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.12s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__row td {
|
|
||||||
padding: 11px 16px;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__row:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__row:hover,
|
|
||||||
.table__row:focus {
|
|
||||||
background: var(--accent-soft);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__name {
|
|
||||||
display: block;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__id {
|
|
||||||
display: block;
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--text-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 12px;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border: 1px solid var(--border-bright);
|
|
||||||
border-radius: 5px;
|
|
||||||
color: var(--accent);
|
|
||||||
background: var(--accent-soft);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__col-amount {
|
|
||||||
font-size: 15px;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table__empty {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--text-dim);
|
|
||||||
padding: 28px 16px !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import FilterBar from '../components/FilterBar.vue';
|
import FilterBar from '../components/FilterBar.vue';
|
||||||
import ItemTable from '../components/ItemTable.vue';
|
import ItemGrid from '../components/ItemGrid.vue';
|
||||||
import { useLiveData } from '../composables/useLiveData.js';
|
import { useLiveData } from '../composables/useLiveData.js';
|
||||||
import type { LiveFilters } from '../api/types.js';
|
import type { LiveFilters } from '../api/types.js';
|
||||||
import { abbreviateNumber, formatRelative } from '../utils/format.js';
|
import { abbreviateNumber, formatRelative } from '../utils/format.js';
|
||||||
@@ -53,7 +53,7 @@ function onFilterUpdate(value: Partial<LiveFilters>): void {
|
|||||||
<span v-else>Waiting for first update…</span>
|
<span v-else>Waiting for first update…</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<ItemTable :items="items" />
|
<ItemGrid :items="items" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user