Fix login button and improve CORS
All checks were successful
Build Frontend / Build Frontend (push) Successful in 22s

- Fixed login URL from /auth/steam to /api/auth/steam
- Updated all Steam login buttons to custom green design with 'Login to Steam' text
- Enhanced CORS configuration with explicit preflight handling
- Added Steam image proxy endpoint for CORS-free image loading
- Improved environment variable management with .env.local support
- Added ENV_SETUP.md guide for environment configuration
This commit is contained in:
2026-01-11 01:39:35 +00:00
parent 91f01cd1cf
commit e7ea8f12b6
9 changed files with 465 additions and 148 deletions

View File

@@ -250,12 +250,17 @@ onUnmounted(() => {
</div>
<!-- Login Button -->
<button v-else @click="handleLogin" class="btn btn-primary">
<img
src="https://community.cloudflare.steamstatic.com/public/images/signinthroughsteam/sits_01.png"
alt="Sign in through Steam"
class="h-6"
/>
<button
v-else
@click="handleLogin"
class="px-6 py-2.5 bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 text-white font-semibold rounded-lg transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-2"
>
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2a10 10 0 0 0-10 10 10 10 0 0 0 10 10c.5 0 1-.04 1.48-.1L8.44 14.3a3.2 3.2 0 0 1-.94-2.27c0-1.77 1.43-3.2 3.2-3.2.53 0 1.03.13 1.47.36L15.5 6.7A9.96 9.96 0 0 0 12 2m6.5 4.5-4.67 2.13c.52.47.84 1.15.84 1.9 0 1.41-1.15 2.57-2.57 2.57-.17 0-.33-.02-.49-.05l-2.25 3.25.02.02c2.25.55 4.77-.51 6.41-2.8 2.02-2.83 1.78-6.7-.29-7.02z"
/>
</svg>
Login to Steam
</button>
<!-- Mobile Menu Toggle -->

View File

@@ -1,223 +1,238 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import axios from 'axios'
import { useToast } from 'vue-toastification'
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import axios from "axios";
import { useToast } from "vue-toastification";
const toast = useToast()
const toast = useToast();
export const useAuthStore = defineStore('auth', () => {
export const useAuthStore = defineStore("auth", () => {
// State
const user = ref(null)
const isAuthenticated = ref(false)
const isLoading = ref(false)
const isInitialized = ref(false)
const user = ref(null);
const isAuthenticated = ref(false);
const isLoading = ref(false);
const isInitialized = ref(false);
// Computed
const username = computed(() => user.value?.username || 'Guest')
const steamId = computed(() => user.value?.steamId || null)
const avatar = computed(() => user.value?.avatar || null)
const balance = computed(() => user.value?.balance || 0)
const staffLevel = computed(() => user.value?.staffLevel || 0)
const isStaff = computed(() => staffLevel.value > 0)
const isModerator = computed(() => staffLevel.value >= 2)
const isAdmin = computed(() => staffLevel.value >= 3)
const tradeUrl = computed(() => user.value?.tradeUrl || null)
const email = computed(() => user.value?.email?.address || null)
const emailVerified = computed(() => user.value?.email?.verified || false)
const isBanned = computed(() => user.value?.ban?.banned || false)
const banReason = computed(() => user.value?.ban?.reason || null)
const twoFactorEnabled = computed(() => user.value?.twoFactor?.enabled || false)
const username = computed(() => user.value?.username || "Guest");
const steamId = computed(() => user.value?.steamId || null);
const avatar = computed(() => user.value?.avatar || null);
const balance = computed(() => user.value?.balance || 0);
const staffLevel = computed(() => user.value?.staffLevel || 0);
const isStaff = computed(() => staffLevel.value > 0);
const isModerator = computed(() => staffLevel.value >= 2);
const isAdmin = computed(() => staffLevel.value >= 3);
const tradeUrl = computed(() => user.value?.tradeUrl || null);
const email = computed(() => user.value?.email?.address || null);
const emailVerified = computed(() => user.value?.email?.verified || false);
const isBanned = computed(() => user.value?.ban?.banned || false);
const banReason = computed(() => user.value?.ban?.reason || null);
const twoFactorEnabled = computed(
() => user.value?.twoFactor?.enabled || false
);
// Actions
const setUser = (userData) => {
user.value = userData
isAuthenticated.value = !!userData
}
user.value = userData;
isAuthenticated.value = !!userData;
};
const clearUser = () => {
user.value = null
isAuthenticated.value = false
}
user.value = null;
isAuthenticated.value = false;
};
const fetchUser = async () => {
if (isLoading.value) return
if (isLoading.value) return;
isLoading.value = true
isLoading.value = true;
try {
const response = await axios.get('/api/auth/me', {
const response = await axios.get("/api/auth/me", {
withCredentials: true,
})
});
if (response.data.success && response.data.user) {
setUser(response.data.user)
return response.data.user
setUser(response.data.user);
return response.data.user;
} else {
clearUser()
return null
clearUser();
return null;
}
} catch (error) {
console.error('Failed to fetch user:', error)
clearUser()
return null
console.error("Failed to fetch user:", error);
clearUser();
return null;
} finally {
isLoading.value = false
isInitialized.value = true
isLoading.value = false;
isInitialized.value = true;
}
}
};
const login = () => {
// Redirect to Steam login
window.location.href = '/api/auth/steam'
}
// Redirect to Steam login on backend API domain
const apiUrl = import.meta.env.VITE_API_URL || "http://localhost:3000";
window.location.href = `${apiUrl}/api/auth/steam`;
};
const logout = async () => {
isLoading.value = true
isLoading.value = true;
try {
await axios.post('/api/auth/logout', {}, {
withCredentials: true,
})
await axios.post(
"/api/auth/logout",
{},
{
withCredentials: true,
}
);
clearUser()
toast.success('Successfully logged out')
clearUser();
toast.success("Successfully logged out");
// Redirect to home page
if (window.location.pathname !== '/') {
window.location.href = '/'
if (window.location.pathname !== "/") {
window.location.href = "/";
}
} catch (error) {
console.error('Logout error:', error)
toast.error('Failed to logout')
console.error("Logout error:", error);
toast.error("Failed to logout");
} finally {
isLoading.value = false
isLoading.value = false;
}
}
};
const refreshToken = async () => {
try {
await axios.post('/api/auth/refresh', {}, {
withCredentials: true,
})
return true
await axios.post(
"/api/auth/refresh",
{},
{
withCredentials: true,
}
);
return true;
} catch (error) {
console.error('Token refresh failed:', error)
clearUser()
return false
console.error("Token refresh failed:", error);
clearUser();
return false;
}
}
};
const updateTradeUrl = async (tradeUrl) => {
isLoading.value = true
isLoading.value = true;
try {
const response = await axios.patch('/api/user/trade-url',
const response = await axios.patch(
"/api/user/trade-url",
{ tradeUrl },
{ withCredentials: true }
)
);
if (response.data.success) {
user.value.tradeUrl = tradeUrl
toast.success('Trade URL updated successfully')
return true
user.value.tradeUrl = tradeUrl;
toast.success("Trade URL updated successfully");
return true;
}
return false
return false;
} catch (error) {
console.error('Failed to update trade URL:', error)
toast.error(error.response?.data?.message || 'Failed to update trade URL')
return false
console.error("Failed to update trade URL:", error);
toast.error(
error.response?.data?.message || "Failed to update trade URL"
);
return false;
} finally {
isLoading.value = false
isLoading.value = false;
}
}
};
const updateEmail = async (email) => {
isLoading.value = true
isLoading.value = true;
try {
const response = await axios.patch('/api/user/email',
const response = await axios.patch(
"/api/user/email",
{ email },
{ withCredentials: true }
)
);
if (response.data.success) {
user.value.email = { address: email, verified: false }
toast.success('Email updated! Check your inbox for verification link')
return true
user.value.email = { address: email, verified: false };
toast.success("Email updated! Check your inbox for verification link");
return true;
}
return false
return false;
} catch (error) {
console.error('Failed to update email:', error)
toast.error(error.response?.data?.message || 'Failed to update email')
return false
console.error("Failed to update email:", error);
toast.error(error.response?.data?.message || "Failed to update email");
return false;
} finally {
isLoading.value = false
isLoading.value = false;
}
}
};
const verifyEmail = async (token) => {
isLoading.value = true
isLoading.value = true;
try {
const response = await axios.get(`/api/user/verify-email/${token}`, {
withCredentials: true
})
withCredentials: true,
});
if (response.data.success) {
toast.success('Email verified successfully!')
await fetchUser() // Refresh user data
return true
toast.success("Email verified successfully!");
await fetchUser(); // Refresh user data
return true;
}
return false
return false;
} catch (error) {
console.error('Failed to verify email:', error)
toast.error(error.response?.data?.message || 'Failed to verify email')
return false
console.error("Failed to verify email:", error);
toast.error(error.response?.data?.message || "Failed to verify email");
return false;
} finally {
isLoading.value = false
isLoading.value = false;
}
}
};
const getUserStats = async () => {
try {
const response = await axios.get('/api/user/stats', {
withCredentials: true
})
const response = await axios.get("/api/user/stats", {
withCredentials: true,
});
if (response.data.success) {
return response.data.stats
return response.data.stats;
}
return null
return null;
} catch (error) {
console.error('Failed to fetch user stats:', error)
return null
console.error("Failed to fetch user stats:", error);
return null;
}
}
};
const getBalance = async () => {
try {
const response = await axios.get('/api/user/balance', {
withCredentials: true
})
const response = await axios.get("/api/user/balance", {
withCredentials: true,
});
if (response.data.success) {
user.value.balance = response.data.balance
return response.data.balance
user.value.balance = response.data.balance;
return response.data.balance;
}
return null
return null;
} catch (error) {
console.error('Failed to fetch balance:', error)
return null
console.error("Failed to fetch balance:", error);
return null;
}
}
};
const updateBalance = (newBalance) => {
if (user.value) {
user.value.balance = newBalance
user.value.balance = newBalance;
}
}
};
// Initialize on store creation
const initialize = async () => {
if (!isInitialized.value) {
await fetchUser()
await fetchUser();
}
}
};
return {
// State
@@ -256,5 +271,5 @@ export const useAuthStore = defineStore('auth', () => {
getBalance,
updateBalance,
initialize,
}
})
};
});

View File

@@ -381,13 +381,14 @@ const getRarityColor = (rarity) => {
<button
v-if="!authStore.isAuthenticated"
@click="authStore.login"
class="btn btn-lg bg-white text-primary-600 hover:bg-gray-100"
class="px-8 py-3.5 bg-gradient-to-r from-green-600 to-green-700 hover:from-green-700 hover:to-green-800 text-white font-semibold rounded-lg transition-all duration-200 shadow-lg hover:shadow-xl flex items-center gap-3 text-lg"
>
<img
src="https://community.cloudflare.steamstatic.com/public/images/signinthroughsteam/sits_01.png"
alt="Sign in through Steam"
class="h-6"
/>
<svg class="w-6 h-6" viewBox="0 0 24 24" fill="currentColor">
<path
d="M12 2a10 10 0 0 0-10 10 10 10 0 0 0 10 10c.5 0 1-.04 1.48-.1L8.44 14.3a3.2 3.2 0 0 1-.94-2.27c0-1.77 1.43-3.2 3.2-3.2.53 0 1.03.13 1.47.36L15.5 6.7A9.96 9.96 0 0 0 12 2m6.5 4.5-4.67 2.13c.52.47.84 1.15.84 1.9 0 1.41-1.15 2.57-2.57 2.57-.17 0-.33-.02-.49-.05l-2.25 3.25.02.02c2.25.55 4.77-.51 6.41-2.8 2.02-2.83 1.78-6.7-.29-7.02z"
/>
</svg>
Login to Steam
</button>
<button
v-else

View File

@@ -53,12 +53,12 @@
<div class="login-section">
<p class="login-prompt">Admin? Login to access the site</p>
<a :href="steamLoginUrl" class="steam-login-btn">
<svg class="steam-icon" viewBox="0 0 256 256" fill="currentColor">
<svg class="steam-icon" viewBox="0 0 24 24" fill="currentColor">
<path
d="M127.999 0C57.421 0 0 57.421 0 127.999c0 63.646 46.546 116.392 107.404 126.284l35.542-51.937c-2.771.413-5.623.631-8.525.631-29.099 0-52.709-23.611-52.709-52.709 0-29.099 23.61-52.709 52.709-52.709 29.098 0 52.708 23.61 52.708 52.709 0 2.902-.218 5.754-.631 8.525l51.937 35.542C248.423 173.536 256 151.997 256 127.999 256 57.421 198.579 0 127.999 0zm-1.369 96.108c-20.175 0-36.559 16.383-36.559 36.559 0 .367.006.732.018 1.096l24.357 10.07c4.023-2.503 8.772-3.951 13.844-3.951 14.576 0 26.418 11.842 26.418 26.418s-11.842 26.418-26.418 26.418c-14.048 0-25.538-10.997-26.343-24.853l-23.554-9.742c.04.832.061 1.669.061 2.51 0 20.175 16.383 36.559 36.559 36.559 20.175 0 36.558-16.384 36.558-36.559 0-20.176-16.383-36.559-36.558-36.559z"
d="M12 2a10 10 0 0 0-10 10 10 10 0 0 0 10 10c.5 0 1-.04 1.48-.1L8.44 14.3a3.2 3.2 0 0 1-.94-2.27c0-1.77 1.43-3.2 3.2-3.2.53 0 1.03.13 1.47.36L15.5 6.7A9.96 9.96 0 0 0 12 2m6.5 4.5-4.67 2.13c.52.47.84 1.15.84 1.9 0 1.41-1.15 2.57-2.57 2.57-.17 0-.33-.02-.49-.05l-2.25 3.25.02.02c2.25.55 4.77-.51 6.41-2.8 2.02-2.83 1.78-6.7-.29-7.02z"
/>
</svg>
<span>Login with Steam</span>
<span>Login to Steam</span>
</a>
</div>
@@ -423,7 +423,7 @@ onUnmounted(() => {
align-items: center;
gap: 0.75rem;
padding: 0.875rem 2rem;
background: linear-gradient(135deg, #171a21 0%, #1b2838 100%);
background: linear-gradient(135deg, #16a34a 0%, #15803d 100%);
color: white;
text-decoration: none;
border-radius: 0.5rem;
@@ -435,7 +435,7 @@ onUnmounted(() => {
}
.steam-login-btn:hover {
background: linear-gradient(135deg, #1b2838 0%, #2a475e 100%);
background: linear-gradient(135deg, #15803d 0%, #166534 100%);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
border-color: rgba(255, 255, 255, 0.2);