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
|
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
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
COPY package*.json ./
|
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
|
||||||
RUN npm run build-haditha
|
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
|
EXPOSE 3000
|
||||||
|
|
||||||
CMD ["node", ".output/server/index.mjs"]
|
CMD ["pm2-runtime", ".output/server/index.mjs"]
|
|
@ -70,7 +70,7 @@ export default defineNuxtConfig({
|
||||||
// "vuejs-paginate",
|
// "vuejs-paginate",
|
||||||
],
|
],
|
||||||
devtools: {
|
devtools: {
|
||||||
enabled: process.env.NODE_ENV === "development",
|
enabled: true,
|
||||||
vscode: {
|
vscode: {
|
||||||
reuseExistingServer: true,
|
reuseExistingServer: true,
|
||||||
},
|
},
|
||||||
|
@ -79,7 +79,7 @@ export default defineNuxtConfig({
|
||||||
features: {
|
features: {
|
||||||
inlineStyles: false,
|
inlineStyles: false,
|
||||||
},
|
},
|
||||||
debug: false,
|
debug: true,
|
||||||
// Modules and plugins
|
// Modules and plugins
|
||||||
modules: [
|
modules: [
|
||||||
"@pinia/nuxt",
|
"@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) => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
const config = useRuntimeConfig();
|
const config = useRuntimeConfig();
|
||||||
|
|
||||||
// const { session } = useUserSession();
|
const id_token = useCookie("id_token");
|
||||||
// const authStore = useAuthStore();
|
const token = id_token.value ?? "GuestAccess";
|
||||||
let token = useStorage("id_token", "GuestAccess").value;
|
|
||||||
|
const baseUrl =
|
||||||
|
config.public.NUXT_PUBLIC_BASE_URL + config.public.NUXT_PUBLIC_API_NAME;
|
||||||
|
|
||||||
const api = $fetch.create({
|
const api = $fetch.create({
|
||||||
|
baseURL: baseUrl,
|
||||||
onRequest({ request, options, error }) {
|
onRequest({ request, options, error }) {
|
||||||
options.baseURL =
|
// options.baseURL = options.baseURL;
|
||||||
config.public.NUXT_PUBLIC_BASE_URL +
|
|
||||||
config.public.NUXT_PUBLIC_API_NAME +
|
|
||||||
options.baseURL;
|
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
const headers = (options.headers ||= {});
|
const headers = (options.headers ||= {});
|
||||||
|
|
||||||
if (Array.isArray(headers)) {
|
if (Array.isArray(headers)) {
|
||||||
headers.push(["Authorization", token]);
|
headers.push(["Authorization", token]);
|
||||||
} else if (headers instanceof Headers) {
|
} else if (headers instanceof Headers) {
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
import { useStorage } from "@vueuse/core";
|
import { useStorage } from "@vueuse/core";
|
||||||
|
|
||||||
export default defineNuxtPlugin((nuxtApp) => {
|
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 config = useRuntimeConfig();
|
||||||
|
|
||||||
const api = $fetch.create({
|
const api = $fetch.create({
|
||||||
|
baseURL: config.public.NUXT_PUBLIC_BASE_URL +
|
||||||
|
config.public.NUXT_PUBLIC_API_NAME,
|
||||||
|
|
||||||
onRequest({ request, options, error }) {
|
onRequest({ request, options, error }) {
|
||||||
options.baseURL =
|
|
||||||
config.public.NUXT_PUBLIC_BASE_URL +
|
|
||||||
config.public.NUXT_PUBLIC_API_NAME +
|
|
||||||
options.baseURL;
|
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
const headers = (options.headers ||= {});
|
const headers = (options.headers ||= {});
|
||||||
if (Array.isArray(headers)) {
|
if (Array.isArray(headers)) {
|
||||||
|
@ -38,7 +37,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||||
|
|
||||||
// Add custom methods for GET, POST, and DELETE
|
// Add custom methods for GET, POST, and DELETE
|
||||||
const http = {
|
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 = {}) =>
|
postRequest: (url: string, body: any, options = {}) =>
|
||||||
api(url, { method: "POST", body, ...options }),
|
api(url, { method: "POST", body, ...options }),
|
||||||
deleteRequest: (url: string, 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