This commit is contained in:
pqdong 2025-05-06 06:37:10 +00:00 committed by GitHub
commit 5557bc20c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 172 additions and 1 deletions

3
.gitignore vendored
View file

@ -32,4 +32,5 @@ coverage
/playwright-report/
/playwright/.cache/
# Webkit with playwright creates a salt file
salt
salt
package-lock.json

18
DockerfileWithToken Normal file
View file

@ -0,0 +1,18 @@
# build stage
FROM node:lts-alpine AS build-stage
# Set environment variables for non-interactive npm installs
ENV NPM_CONFIG_LOGLEVEL warn
ENV CI true
ENV VITE_TOKEN "your_token"
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm i --frozen-lockfile
COPY . .
RUN pnpm build
# production stage
FROM nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

View file

@ -34,6 +34,15 @@ docker run -d --name it-tools --restart unless-stopped -p 8080:80 ghcr.io/corent
- [Tipi](https://www.runtipi.io/docs/apps-available)
- [Unraid](https://unraid.net/community/apps?q=it-tools)
## Public
If you want to add permission verification when users access the website, you can build and run the image using the following method. This approach will require users to enter a token before they can continue using the site.
```sh
cd it-tools
# vim DockerfileWithToken, change your_token to the token you want to use
docker build -f DockerfileWithToken -t it-tools:token .
docker run -d --name it-tools --restart unless-stopped -p 8080:80 it-tools:token
```
## Contribute
### Recommended IDE Setup

1
components.d.ts vendored
View file

@ -118,6 +118,7 @@ declare module '@vue/runtime-core' {
KeycodeInfo: typeof import('./src/tools/keycode-info/keycode-info.vue')['default']
ListConverter: typeof import('./src/tools/list-converter/list-converter.vue')['default']
LocaleSelector: typeof import('./src/modules/i18n/components/locale-selector.vue')['default']
Login: typeof import('./src/pages/Login.vue')['default']
LoremIpsumGenerator: typeof import('./src/tools/lorem-ipsum-generator/lorem-ipsum-generator.vue')['default']
MacAddressGenerator: typeof import('./src/tools/mac-address-generator/mac-address-generator.vue')['default']
MacAddressLookup: typeof import('./src/tools/mac-address-lookup/mac-address-lookup.vue')['default']

View file

@ -27,6 +27,12 @@ export const config = figue({
default: 'development',
env: 'VITE_VERCEL_ENV',
},
token: {
doc: 'Application token',
format: 'string',
default: '',
env: 'VITE_TOKEN',
},
},
plausible: {
isTrackerEnabled: {

120
src/pages/Login.vue Normal file
View file

@ -0,0 +1,120 @@
<script setup lang="ts">
import { ref } from 'vue';
import { config } from '@/config';
const token = ref('');
async function handleLogin() {
if (config.app.token === token.value) {
localStorage.setItem('isLoggedIn', 'true');
window.location.href = `${window.location.protocol}//${window.location.host}/`;
} else {
alert('Invalid Token');
}
}
</script>
<template>
<div class="login-container">
<div class="login-card">
<h1 class="login-title">Login</h1>
<form @submit.prevent="handleLogin" class="login-form">
<div class="form-group">
<label for="token" class="form-label">Token:</label>
<input
id="token"
v-model="token"
type="text"
required
class="form-input"
placeholder="Enter your access token"
/>
</div>
<button type="submit" class="login-button">Login</button>
</form>
</div>
</div>
</template>
<style scoped>
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f5f5f5;
padding: 20px;
}
.login-card {
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 40px;
width: 100%;
max-width: 400px;
}
.login-title {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
font-size: 24px;
font-weight: 600;
}
.login-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-label {
color: #2c3e50;
font-size: 14px;
font-weight: 500;
}
.form-input {
padding: 12px 16px;
border: 1px solid #e0e0e0;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-input:focus {
outline: none;
border-color: #4CAF50;
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2);
}
.login-button {
background: linear-gradient(135deg, #4CAF50, #81C784);
color: white;
border: none;
border-radius: 6px;
padding: 12px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
margin-top: 10px;
}
.login-button:hover {
background: linear-gradient(135deg, #3e8e41, #66BB6A);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.login-button:active {
transform: translateY(0);
}
</style>

View file

@ -26,6 +26,11 @@ const router = createRouter({
name: 'home',
component: HomePage,
},
{
path: '/login',
name: 'login',
component: () => import('./pages/Login.vue'),
},
{
path: '/about',
name: 'about',
@ -38,4 +43,15 @@ const router = createRouter({
],
});
if (config.app.token){
router.beforeEach((to, from, next) => {
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
if (to.path !== '/login' && !isLoggedIn) {
next('/login');
} else {
next();
}
});
}
export default router;