Remove server folder files.
Update nuxt.config Add new composables.
This commit is contained in:
parent
2e0d14ab61
commit
ff3469a271
|
@ -1,4 +1,5 @@
|
|||
NUXT_PUBLIC_BASE_URL=http://192.168.23.161/
|
||||
# NUXT_PUBLIC_BASE_URL=http://192.168.23.161/
|
||||
NUXT_PUBLIC_BASE_URL=https://hadithai.ir/
|
||||
|
||||
VITE_APP_NAME=Hadith
|
||||
|
||||
|
|
11
composables/useApi.ts
Normal file
11
composables/useApi.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import type { UseFetchOptions } from 'nuxt/app'
|
||||
|
||||
export function useApi<T>(
|
||||
url: string | (() => string),
|
||||
options?: UseFetchOptions<T>,
|
||||
) {
|
||||
return useFetch(url, {
|
||||
...options,
|
||||
$fetch: useNuxtApp().$api as typeof $fetch
|
||||
})
|
||||
}
|
81
composables/useFetchService.ts
Normal file
81
composables/useFetchService.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import type { FetchOptions } from 'ofetch'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
|
||||
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
||||
|
||||
interface HttpRequestOptions<T = unknown> extends FetchOptions {
|
||||
data?: T
|
||||
}
|
||||
|
||||
export const useHttpService = () => {
|
||||
const config = useRuntimeConfig()
|
||||
const token = useStorage('id_token', 'GuestAccess')
|
||||
|
||||
/**
|
||||
* Constructs the full API URL
|
||||
*/
|
||||
const getFullUrl = (endpoint: string): string => {
|
||||
return `${config.public.NUXT_PUBLIC_BASE_URL}${config.public.NUXT_PUBLIC_API_NAME}${endpoint}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Base fetch method with common configuration
|
||||
*/
|
||||
const baseFetch = async <T = any>(
|
||||
method: HttpMethod,
|
||||
url: string,
|
||||
options: HttpRequestOptions = {}
|
||||
) => {
|
||||
const headers = {
|
||||
...options.headers,
|
||||
...(token.value ? { Authorization: token.value } : {})
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await $fetch<T>(getFullUrl(url), {
|
||||
method,
|
||||
headers,
|
||||
...options,
|
||||
body: options.data || options.body
|
||||
})
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
// Handle errors globally or rethrow for specific handling
|
||||
console.error(`HTTP ${method} error for ${url}:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* GET request
|
||||
*/
|
||||
getRequest: <T = any>(url: string, options?: HttpRequestOptions) =>
|
||||
baseFetch<T>('GET', url, options),
|
||||
|
||||
/**
|
||||
* POST request
|
||||
*/
|
||||
postRequest: <T = any>(url: string, data?: any, options?: HttpRequestOptions) =>
|
||||
baseFetch<T>('POST', url, { ...options, data }),
|
||||
|
||||
/**
|
||||
* PUT request
|
||||
*/
|
||||
putRequest: <T = any>(url: string, data?: any, options?: HttpRequestOptions) =>
|
||||
baseFetch<T>('PUT', url, { ...options, data }),
|
||||
|
||||
/**
|
||||
* PATCH request
|
||||
*/
|
||||
patchRequest: <T = any>(url: string, data?: any, options?: HttpRequestOptions) =>
|
||||
baseFetch<T>('PATCH', url, { ...options, data }),
|
||||
|
||||
/**
|
||||
* DELETE request
|
||||
*/
|
||||
deleteRequest: <T = any>(url: string, options?: HttpRequestOptions) =>
|
||||
baseFetch<T>('DELETE', url, options)
|
||||
}
|
||||
}
|
49
composables/useInfiniteScroll.ts
Normal file
49
composables/useInfiniteScroll.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
// composables/useInfiniteScroll.ts
|
||||
export default function useInfiniteScroll(
|
||||
callback: () => void,
|
||||
elementId: string
|
||||
) {
|
||||
const isFetching = ref(false);
|
||||
const infiniteScroll = ref<HTMLElement | null>(null);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (isFetching.value) return;
|
||||
const scrollPosition =
|
||||
infiniteScroll.value.scrollTop + infiniteScroll.value.clientHeight;
|
||||
const threshold = infiniteScroll.value.scrollHeight - 100;
|
||||
|
||||
if (scrollPosition >= threshold) {
|
||||
isFetching.value = true;
|
||||
callback().finally(() => {
|
||||
isFetching.value = false;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
// Add a slight delay to ensure scroll position is updated
|
||||
setTimeout(handleScroll, 100);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const targetElement = document.getElementById(elementId);
|
||||
infiniteScroll.value = targetElement;
|
||||
|
||||
if (targetElement) {
|
||||
targetElement.addEventListener("scroll", handleScroll);
|
||||
targetElement.addEventListener("touchend", handleTouchEnd);
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const targetElement = document.getElementById(elementId);
|
||||
infiniteScroll.value = targetElement;
|
||||
|
||||
if (targetElement) {
|
||||
targetElement.removeEventListener("scroll", handleScroll);
|
||||
targetElement.removeEventListener("touchend", handleTouchEnd);
|
||||
}
|
||||
});
|
||||
|
||||
return { isFetching };
|
||||
}
|
46
composables/useInfiniteScrollObserver.ts
Normal file
46
composables/useInfiniteScrollObserver.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
// composables/useInfiniteScrollObserver.ts
|
||||
import { onBeforeUnmount, onMounted, ref } from "vue";
|
||||
|
||||
export default function useInfiniteScrollObserver(callback) {
|
||||
const observer = ref<IntersectionObserver | null>(null);
|
||||
const isFetching = ref(false);
|
||||
const infiniteScroll = ref<HTMLElement | null>(null);
|
||||
console.info("useInfiniteScrollObserver");
|
||||
|
||||
const initObserver = () => {
|
||||
console.info("useInfiniteScrollObserver");
|
||||
|
||||
observer.value = new IntersectionObserver(
|
||||
(entries) => {
|
||||
console.info("useInfiniteScrollObserver");
|
||||
|
||||
if (entries[0].isIntersecting && !isFetching.value) {
|
||||
isFetching.value = true;
|
||||
callback().finally(() => {
|
||||
isFetching.value = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: "200px", // Load when 200px away from viewport
|
||||
threshold: 0.1,
|
||||
}
|
||||
);
|
||||
|
||||
if (infiniteScroll.value) {
|
||||
observer.value.observe(infiniteScroll.value);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initObserver();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (observer.value && infiniteScroll.value) {
|
||||
observer.value.unobserve(infiniteScroll.value);
|
||||
}
|
||||
});
|
||||
|
||||
return { isFetching, infiniteScroll };
|
||||
}
|
11
dockerfile
11
dockerfile
|
@ -2,13 +2,16 @@ FROM node:22
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
COPY package*.json ./
|
||||
COPY . .
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm run build-haditha
|
||||
|
||||
RUN npm install -g pm2
|
||||
RUN pm2 start .output/server/index.mjs --name "nuxt-app"
|
||||
RUN pm2 save
|
||||
RUN pm2 startup
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", ".output/server/index.mjs"]
|
||||
CMD ["pm2-runtime", ".output/server/index.mjs"]
|
|
@ -70,7 +70,7 @@ export default defineNuxtConfig({
|
|||
// "vuejs-paginate",
|
||||
],
|
||||
devtools: {
|
||||
enabled: process.env.NODE_ENV === "development",
|
||||
enabled: true,
|
||||
vscode: {
|
||||
reuseExistingServer: true,
|
||||
},
|
||||
|
@ -79,7 +79,7 @@ export default defineNuxtConfig({
|
|||
features: {
|
||||
inlineStyles: false,
|
||||
},
|
||||
debug: false,
|
||||
debug: true,
|
||||
// Modules and plugins
|
||||
modules: [
|
||||
"@pinia/nuxt",
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
import { useAuthStore } from "~/stores/authStore";
|
||||
import { useStorage } from "@vueuse/core";
|
||||
|
||||
// let lsToken = useStorage("token", "GuestAccess");
|
||||
// if (lsToken == null || lsToken == "" || lsToken == undefined)
|
||||
// lsToken = "GuestAccess";
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
// const { session } = useUserSession();
|
||||
// const authStore = useAuthStore();
|
||||
let token = useStorage("id_token", "GuestAccess").value;
|
||||
const id_token = useCookie("id_token");
|
||||
const token = id_token.value ?? "GuestAccess";
|
||||
|
||||
const baseUrl =
|
||||
config.public.NUXT_PUBLIC_BASE_URL + config.public.NUXT_PUBLIC_API_NAME;
|
||||
|
||||
const api = $fetch.create({
|
||||
baseURL: baseUrl,
|
||||
onRequest({ request, options, error }) {
|
||||
options.baseURL =
|
||||
config.public.NUXT_PUBLIC_BASE_URL +
|
||||
config.public.NUXT_PUBLIC_API_NAME +
|
||||
options.baseURL;
|
||||
// options.baseURL = options.baseURL;
|
||||
|
||||
if (token) {
|
||||
const headers = (options.headers ||= {});
|
||||
|
||||
if (Array.isArray(headers)) {
|
||||
headers.push(["Authorization", token]);
|
||||
} else if (headers instanceof Headers) {
|
||||
|
|
|
@ -2,16 +2,15 @@
|
|||
import { useStorage } from "@vueuse/core";
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
let token = useStorage("id_token", "GuestAccess").value;
|
||||
// let token = useStorage("id_token", "GuestAccess").value;
|
||||
let token = 'GuestAccess';
|
||||
const config = useRuntimeConfig();
|
||||
|
||||
const api = $fetch.create({
|
||||
onRequest({ request, options, error }) {
|
||||
options.baseURL =
|
||||
config.public.NUXT_PUBLIC_BASE_URL +
|
||||
config.public.NUXT_PUBLIC_API_NAME +
|
||||
options.baseURL;
|
||||
baseURL: config.public.NUXT_PUBLIC_BASE_URL +
|
||||
config.public.NUXT_PUBLIC_API_NAME,
|
||||
|
||||
onRequest({ request, options, error }) {
|
||||
if (token) {
|
||||
const headers = (options.headers ||= {});
|
||||
if (Array.isArray(headers)) {
|
||||
|
@ -38,7 +37,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||
|
||||
// Add custom methods for GET, POST, and DELETE
|
||||
const http = {
|
||||
getRequest: (url: string, options = {}) => api(url, { method: "GET", ...options }),
|
||||
getRequest: (url: string, options = {}) =>
|
||||
api(url, { method: "GET", ...options }),
|
||||
postRequest: (url: string, body: any, options = {}) =>
|
||||
api(url, { method: "POST", body, ...options }),
|
||||
deleteRequest: (url: string, options = {}) =>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// server/api/login.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
// const body = await readBody(event);
|
||||
// const response = await $fetch("https://api.example.com/login", {
|
||||
// method: "POST",
|
||||
// body,
|
||||
// });
|
||||
// return response;
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
// server/api/users/[id].ts
|
||||
export default defineEventHandler((event) => {
|
||||
// const id = event.context.params?.id;
|
||||
// return { userId: id };
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
// server/api/data.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
// const data = await $fetch("https://api.example.com/data", {
|
||||
// headers: { Authorization: "Bearer my-secret-token" },
|
||||
// });
|
||||
// return data;
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
// server/api/data.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
// const data = await $fetch("https://api.example.com/data", {
|
||||
// headers: { Authorization: "Bearer my-secret-token" },
|
||||
// });
|
||||
// return data;
|
||||
});
|
|
@ -1,7 +0,0 @@
|
|||
// server/api/data.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
// const data = await $fetch("https://api.example.com/data", {
|
||||
// headers: { Authorization: "Bearer my-secret-token" },
|
||||
// });
|
||||
// return data;
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
import { defineEventHandler, readMultipartFormData } from "h3";
|
||||
import { promises as fs } from "fs";
|
||||
import sharp from "sharp";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const files = await readMultipartFormData(event);
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
return { error: "No files uploaded" };
|
||||
}
|
||||
|
||||
const uploadedFile = files[0];
|
||||
const fileName = `cropped-${Date.now()}.png`;
|
||||
const filePath = `./public/uploads/${fileName}`;
|
||||
|
||||
// Save the cropped image
|
||||
await fs.writeFile(filePath, uploadedFile.data);
|
||||
|
||||
// Optionally resize the image
|
||||
const resizedFilePath = `./public/uploads/resized-${fileName}`;
|
||||
await sharp(uploadedFile.data)
|
||||
.resize(300, 300) // Adjust dimensions as needed
|
||||
.toFile(resizedFilePath);
|
||||
|
||||
return {
|
||||
message: "File uploaded successfully",
|
||||
originalFile: `/uploads/${fileName}`,
|
||||
resizedFile: `/uploads/resized-${fileName}`,
|
||||
};
|
||||
});
|
|
@ -1,70 +0,0 @@
|
|||
const users = [
|
||||
{
|
||||
user_id: "583c3ac3f38e84297c002546",
|
||||
email: "test@test.com",
|
||||
name: "test@test.com",
|
||||
given_name: "Hello",
|
||||
family_name: "Test",
|
||||
nickname: "test",
|
||||
last_ip: "94.121.163.63",
|
||||
logins_count: 15,
|
||||
created_at: "2016-11-28T14:10:11.338Z",
|
||||
updated_at: "2016-12-02T01:17:29.310Z",
|
||||
last_login: "2016-12-02T01:17:29.310Z",
|
||||
email_verified: true,
|
||||
},
|
||||
{
|
||||
user_id: "583c5484cb79a5fe593425a9",
|
||||
email: "test1@test.com",
|
||||
name: "test1@test.com",
|
||||
given_name: "Hello1",
|
||||
family_name: "Test1",
|
||||
nickname: "test1",
|
||||
last_ip: "94.121.168.53",
|
||||
logins_count: 1,
|
||||
created_at: "2016-11-28T16:00:04.209Z",
|
||||
updated_at: "2016-11-28T16:00:47.203Z",
|
||||
last_login: "2016-11-28T16:00:47.203Z",
|
||||
email_verified: true,
|
||||
},
|
||||
{
|
||||
user_id: "583c57672c7686377d2f66c9",
|
||||
email: "aaa@aaa.com",
|
||||
name: "aaa@aaa.com",
|
||||
given_name: "John",
|
||||
family_name: "Dough",
|
||||
nickname: "aaa",
|
||||
last_ip: "94.121.168.53",
|
||||
logins_count: 2,
|
||||
created_at: "2016-11-28T16:12:23.777Z",
|
||||
updated_at: "2016-11-28T16:12:52.353Z",
|
||||
last_login: "2016-11-28T16:12:52.353Z",
|
||||
email_verified: true,
|
||||
},
|
||||
{
|
||||
user_id: "5840b954da0529cd293d76fe",
|
||||
email: "a@a.com",
|
||||
name: "a@a.com",
|
||||
given_name: "Jane",
|
||||
family_name: "Dough",
|
||||
nickname: "a",
|
||||
last_ip: "94.121.163.63",
|
||||
logins_count: 3,
|
||||
created_at: "2016-12-01T23:59:16.473Z",
|
||||
updated_at: "2016-12-01T23:59:53.474Z",
|
||||
last_login: "2016-12-01T23:59:53.474Z",
|
||||
email_verified: true,
|
||||
},
|
||||
{
|
||||
user_id: "584a9d13e808bcf75f05f580",
|
||||
email: "test9999@test.com",
|
||||
given_name: "Dummy",
|
||||
family_name: "User",
|
||||
created_at: "2016-12-09T12:01:23.787Z",
|
||||
updated_at: "2016-12-09T12:01:23.787Z",
|
||||
email_verified: false,
|
||||
},
|
||||
];
|
||||
export default defineEventHandler((event) => {
|
||||
return users;
|
||||
});
|
|
@ -1 +1 @@
|
|||
Subproject commit dc2d5cc460d983ddcb1b302f238070a79d818135
|
||||
Subproject commit 917824f098aff05dc7ece05c8e159a6bbf1fd35c
|
Loading…
Reference in New Issue
Block a user