Skip to content

Commit cb4508c

Browse files
committed
feat(docker): add docker config and environment variables
1 parent c06bbf1 commit cb4508c

8 files changed

Lines changed: 467 additions & 0 deletions

File tree

.dockerignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
node_modules
2+
Dockerfile*
3+
docker-compose*
4+
.dockerignore
5+
.git
6+
.gitignore
7+
README.md
8+
LICENSE
9+
.vscode
10+
Makefile
11+
helm-charts
12+
.env
13+
.editorconfig
14+
.idea
15+
coverage*

.github/workflows/deploy.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Deploy
2+
3+
on:
4+
workflow_dispatch:
5+
6+
env:
7+
REGISTRY: ghcr.io
8+
IMAGE_NAME: labsyncro/labsyncro
9+
ORG_USERNAME: ${{ github.actor }}
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
issues: write
15+
packages: write
16+
17+
jobs:
18+
build:
19+
name: Build Docker Image
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Checkout Code
23+
uses: actions/checkout@v4
24+
25+
- name: Setup Bun
26+
uses: oven-sh/setup-bun@v2
27+
with:
28+
bun-version: "latest"
29+
30+
- name: Setup Environment Variables
31+
run: |
32+
echo DATABASE_USER=${{ secrets.DATABASE_USER }} >> .env
33+
echo DATABASE_HOST=${{ secrets.DATABASE_HOST }} >> .env
34+
echo DATABASE_NAME=${{ secrets.DATABASE_NAME }} >> .env
35+
echo DATABASE_PASSWORD=${{ secrets.DATABASE_PASSWORD }} >> .env
36+
echo DATABASE_PORT=${{ secrets.DATABASE_PORT }} >> .env
37+
echo JWT_SECRET=${{ secrets.JWT_SECRET }} >> .env
38+
echo GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }} >> .env
39+
echo GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }} >> .env
40+
echo NODE_ENV=${{ secrets.NODE_ENV }} >> .env
41+
echo AUTH_SECRET=${{ secrets.AUTH_SECRET }} >> .env
42+
echo DATABASE_URL=${{ secrets.DATABASE_URL }} >> .env
43+
echo PRINT_LABELS_ENDPOINT=${{ secrets.PRINT_LABELS_ENDPOINT }} >> .env
44+
45+
- name: Setup Docker Buildx
46+
uses: docker/setup-buildx-action@v3
47+
48+
- name: Log into GitHub Container Registry
49+
uses: docker/login-action@v3
50+
with:
51+
registry: ${{ env.REGISTRY }}
52+
username: ${{ env.ORG_USERNAME }}
53+
password: ${{ secrets.GITHUB_TOKEN }}
54+
55+
- name: Build and Push Docker Image
56+
uses: docker/build-push-action@v6
57+
with:
58+
context: .
59+
push: true
60+
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
61+
cache-from: type=gha
62+
cache-to: type=gha,mode=max
63+
64+
deploy:
65+
name: Deploy to VPS
66+
runs-on: ubuntu-latest
67+
needs: [build]
68+
steps:
69+
- name: Deploy Application via SSH
70+
uses: appleboy/ssh-action@master
71+
with:
72+
host: ${{ secrets.VPS_HOST }}
73+
port: ${{ secrets.VPS_PORT }}
74+
username: ${{ secrets.VPS_USERNAME }}
75+
password: ${{ secrets.VPS_PASSWORD }}
76+
script: |
77+
export CR_PAT=${{ secrets.GITHUB_TOKEN }}
78+
echo $CR_PAT | docker login ${{ env.REGISTRY }} -u ${{ env.ORG_USERNAME }} --password-stdin
79+
docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
80+
81+
cd labsyncro
82+
83+
curl -s https://raw.githubusercontent.com/LabSyncro/LabSyncro/main/docker-compose.yml -o docker-compose.yml
84+
85+
docker compose stop labsyncro
86+
docker compose rm -f labsyncro
87+
88+
docker compose up -d labsyncro
89+
90+
docker logout ${{ env.REGISTRY }}

Dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
FROM oven/bun:latest AS base
2+
WORKDIR /usr/src/app
3+
4+
FROM base AS install
5+
RUN mkdir -p /temp/dev
6+
COPY package.json bun.lockb /temp/dev/
7+
RUN cd /temp/dev && bun install --frozen-lockfile
8+
9+
RUN mkdir -p /temp/prod
10+
COPY package.json bun.lockb /temp/prod/
11+
RUN cd /temp/prod && bun install --frozen-lockfile --production
12+
13+
FROM base AS prerelease
14+
COPY --from=install /temp/dev/node_modules node_modules
15+
COPY . .
16+
17+
ENV NODE_ENV=production
18+
RUN bun run build
19+
20+
FROM base AS release
21+
COPY --chown=bun:bun --from=install /temp/prod/node_modules node_modules
22+
COPY --chown=bun:bun --from=prerelease /usr/src/app/.output .
23+
24+
USER bun
25+
ENV NUXT_HOST=0.0.0.0
26+
ENV NUXT_PORT=3000
27+
EXPOSE 3000/tcp
28+
ENTRYPOINT [ "bun", "run", "server/index.mjs" ]
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<script setup lang="ts">
2+
import { deviceKindService } from '~/services';
3+
4+
const emits = defineEmits<{
5+
'device-select': [any],
6+
}>();
7+
8+
const searchText = ref('');
9+
const { isActive: isDropdownActive, setInactive } = useClick(useTemplateRef('dropdown'));
10+
11+
const focusedSearchItemIndex = ref<number | null>(null);
12+
const numberOfSearchItemsShown = 6;
13+
const searchItems = ref<{ id: string; name: string; mainImage: string, subImages: string[], category: string }[]>([]);
14+
watch(searchText, async () => {
15+
focusedSearchItemIndex.value = null;
16+
if (searchText.value === '') {
17+
searchItems.value = [];
18+
return;
19+
}
20+
const data = await deviceKindService.getDeviceKinds(0, numberOfSearchItemsShown, { searchText: searchText.value || undefined, searchFields: ['device_name', 'device_id'] });
21+
searchItems.value = data.deviceKinds.map(({
22+
name,
23+
mainImage,
24+
subImages,
25+
id,
26+
category,
27+
brand,
28+
description
29+
}) => ({
30+
name,
31+
mainImage,
32+
subImages,
33+
id,
34+
category,
35+
brand,
36+
description
37+
}));
38+
});
39+
function focusNextSearchItem () {
40+
if (focusedSearchItemIndex.value === null) focusedSearchItemIndex.value = -1;
41+
focusedSearchItemIndex.value = (focusedSearchItemIndex.value + 1) % numberOfSearchItemsShown;
42+
}
43+
function focusPrevSearchItem () {
44+
if (focusedSearchItemIndex.value === null) focusedSearchItemIndex.value = 0;
45+
focusedSearchItemIndex.value = (focusedSearchItemIndex.value - 1 + numberOfSearchItemsShown) % numberOfSearchItemsShown;
46+
}
47+
function goToSearchItem (deviceInfo: any) {
48+
searchText.value = deviceInfo.name;
49+
setInactive();
50+
emits('device-select', deviceInfo);
51+
}
52+
function unfocusSearchItem () {
53+
focusedSearchItemIndex.value = null;
54+
}
55+
</script>
56+
57+
<template>
58+
<div ref="dropdown" class="relative">
59+
<div class="relative">
60+
<input
61+
v-model="searchText"
62+
class="bg-white text-primary-light placeholder:text-primary-light border-2 h-11 w-[100%] pl-10 pr-3 rounded-md text-md placeholder:text-normal"
63+
type="search" placeholder="Tên/Mã loại thiết bị" @keydown.down="focusNextSearchItem"
64+
@keydown.up="focusPrevSearchItem"
65+
@keydown.enter="focusedSearchItemIndex !== null && goToSearchItem(searchItems[focusedSearchItemIndex!])"
66+
@keydown.esc="unfocusSearchItem">
67+
<Icon
68+
aria-hidden class="absolute left-3 top-[12px] text-xl text-primary-dark"
69+
name="i-heroicons-magnifying-glass" />
70+
</div>
71+
72+
<div
73+
:class="`${isDropdownActive && searchItems.length ? 'flex' : 'hidden'} flex-col gap-1 absolute bg-white p-1 mt-1 w-[120%] z-50 shadow-[0_0px_16px_-3px_rgba(0,0,0,0.3)]`">
74+
<a
75+
v-for="(item, index) in searchItems" :key="item.id"
76+
:class="`cursor-pointer px-2 text-normal p-1 flex justify-start gap-2 hover:bg-gray-100 ${focusedSearchItemIndex === index ? 'bg-secondary-light' : ''}`"
77+
@click="goToSearchItem(searchItems[index])">
78+
<img :src="item.mainImage" class="h-6 w-6 block">
79+
<p
80+
class="p-1 px-2 text-nowrap bg-gray-100 border border-gray-300 rounded-md text-normal font-normal leading-none">
81+
{{ item.id.toUpperCase() }}
82+
</p>
83+
<HighlightText class="line-clamp-1" :text="item.name" :match-text="searchText || undefined" />
84+
</a>
85+
</div>
86+
</div>
87+
</template>

docker-compose.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
version: "3.8"
2+
3+
services:
4+
app:
5+
build:
6+
context: .
7+
dockerfile: Dockerfile
8+
image: ghcr.io/labsyncro/labsyncro
9+
container_name: labsyncro
10+
ports:
11+
- "3000:3000"
12+
environment:
13+
- NODE_ENV=${NODE_ENV}
14+
- DATABASE_USER=${DATABASE_USER}
15+
- DATABASE_HOST=${DATABASE_HOST}
16+
- DATABASE_NAME=${DATABASE_NAME}
17+
- DATABASE_PASSWORD=${DATABASE_PASSWORD}
18+
- DATABASE_PORT=${DATABASE_PORT}
19+
- DATABASE_URL=${DATABASE_URL}
20+
- JWT_SECRET=${JWT_SECRET}
21+
- AUTH_SECRET=${AUTH_SECRET}
22+
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
23+
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
24+
env_file:
25+
- .env

nuxt.config.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ export default defineNuxtConfig({
1717
auth: {
1818
isEnabled: true,
1919
disableServerSideAuth: false,
20+
<<<<<<< Updated upstream
21+
=======
22+
// baseURL:
23+
// 'https://scoring-sponsored-newark-driven.trycloudflare.com/api/auth',
24+
>>>>>>> Stashed changes
2025
baseURL: 'http://localhost:3000/api/auth',
2126
provider: {
2227
type: 'authjs',
@@ -65,6 +70,7 @@ export default defineNuxtConfig({
6570
DATABASE_PORT: process.env.DATABASE_PORT,
6671
JWT_SECRET: process.env.JWT_SECRET,
6772
NODE_ENV: process.env.NODE_ENV,
73+
PRINT_LABELS_URL: process.env.PRINT_LABELS_URL,
6874
},
6975
app: {
7076
head: {

0 commit comments

Comments
 (0)