Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions .github/workflows/linter_checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,23 @@ name: Linter Check
on: push

jobs:
lint:
name: Linter Check
runs-on: ubuntu-latest
lint:
name: Linter Check
runs-on: ubuntu-latest

steps:
- name: Git Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
steps:
- name: Git Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Installing packages
run: yarn
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
version: latest

- name: Prettier/ESLint Check
run: yarn lint
- name: Installing packages
run: bun install --dev

- name: Prettier/ESLint Check
run: bun run lint
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
FROM node:23-alpine
FROM oven/bun:1.2.22-alpine

WORKDIR /app

COPY package*.json yarn.lock ./
COPY package.json bun.lock ./

RUN yarn
RUN bun install

COPY . .

RUN yarn build
RUN bun run build

EXPOSE 3000

CMD ["yarn", "start"]
CMD ["bun", "run", "start"]
69 changes: 33 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
# NuxtJS Template

Project built with Nuxt.js that serves as the default template for initializing my new projects.

## Dependencies

| Package | Version |
|----------|----------|
| `@nuxt/fonts` | 0.11.4 |
| `@nuxt/icon` | 2.1.0 |
| `@nuxt/image` | 1.11.0 |
| `@nuxt/scripts` | 0.13.0 |
| `@nuxt/eslint` | 1.10.0 |
| `@nuxt/test-utils` | 3.20.1 |
| **`@pinia/nuxt`** | ^0.11.2 |
| `@unhead/vue` | ^2.0.19 |
| **`@tailwindcss/vite`** | ^4.1.16 |
| `nuxt` | ^4.2.0 |
| **`nuxt-toast`** | ^1.4.0 |
| **`izitoast`** | ^1.4.0 |
| `vue` | ^3.5.22 |
| `vue-tsc` | ^3.1.3 |
| `eslint` | ^9.39.0 |
| `eslint-config-prettier` | ^10.1.8 |
| `eslint-plugin-prettier` | ^5.5.4 |
| `prettier` | ^3.6.2 |
| **`pinia`** | ^3.0.3 |
| **`pinia-plugin-persistedstate`** | ^4.7.1 |
| **`tailwind-merge`** | ^3.3.1 |
| **`zod`** | ^4.1.12 |
| **`tailwindcss`** | ^4.1.16 |
| **`lodash-es`** | ^4.17.21 |

# Nuxt Minimal Starter

Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
Expand All @@ -7,15 +40,6 @@ Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduct
Make sure to install dependencies:

```bash
# npm
npm install

# pnpm
pnpm install

# yarn
yarn install

# bun
bun install
```
Expand All @@ -25,15 +49,6 @@ bun install
Start the development server on `http://localhost:3000`:

```bash
# npm
npm run dev

# pnpm
pnpm dev

# yarn
yarn dev

# bun
bun run dev
```
Expand All @@ -43,31 +58,13 @@ bun run dev
Build the application for production:

```bash
# npm
npm run build

# pnpm
pnpm build

# yarn
yarn build

# bun
bun run build
```

Locally preview production build:

```bash
# npm
npm run preview

# pnpm
pnpm preview

# yarn
yarn preview

# bun
bun run preview
```
Expand Down
File renamed without changes.
14 changes: 14 additions & 0 deletions app/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { config } from 'zod';
import { pt } from 'zod/locales';

if (import.meta.client) {
config(pt());
}
</script>

<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion src/utils/index.ts → app/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { twMerge, type ClassNameValue } from 'tailwind-merge';
import type { H3Event } from 'h3';
import type { IFetchNativeResponseError, IFetchResponseError } from '~/server/types';
import type { IFetchNativeResponseError, IFetchResponseError } from '~~/server/types';

export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

Expand Down
821 changes: 403 additions & 418 deletions bun.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ export default defineNuxtConfig({
},
devtools: { enabled: true, telemetry: false, timeline: { enabled: true } },
css: ['~/assets/css/main.css'],
srcDir: 'src/',
dir: {
public: '../public',
app: 'app',
public: 'public',
middleware: 'middlewares',
},
components: [
{
Expand Down
39 changes: 20 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "nuxt-app",
"version": "0.2.0",
"name": "nuxtjs-template",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "nuxt dev --host --dotenv .env.local",
"dev": "nuxt dev --dotenv .env.local",
"build": "nuxt build",
"start": "bun .output/server/index.mjs",
"generate": "nuxt generate",
Expand All @@ -15,33 +15,34 @@
"fix": "prettier ./ --write && eslint . --fix && nuxt typecheck"
},
"dependencies": {
"@nuxt/eslint": "1.9.0",
"@nuxt/fonts": "0.11.4",
"@nuxt/icon": "2.0.0",
"@nuxt/icon": "2.1.0",
"@nuxt/image": "1.11.0",
"@nuxt/scripts": "0.11.13",
"@nuxt/test-utils": "3.19.2",
"@nuxt/scripts": "0.13.0",
"@pinia/nuxt": "^0.11.2",
"@tailwindcss/vite": "^4.1.13",
"@unhead/vue": "^2.0.17",
"lodash": "^4.17.21",
"@unhead/vue": "^2.0.19",
"izitoast": "^1.4.0",
"lodash-es": "^4.17.21",
"nuxt": "^4.1.2",
"nuxt-toast": "^1.1.4",
"nuxt": "^4.2.0",
"nuxt-toast": "^1.4.0",
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"pinia-plugin-persistedstate": "^4.7.1",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.13",
"vue": "^3.5.21",
"vue-router": "^4.5.1",
"zod": "^4.1.11"
"vue": "^3.5.22",
"zod": "^4.1.12"
},
"devDependencies": {
"@nuxt/eslint": "1.10.0",
"@nuxt/test-utils": "3.20.1",
"@tailwindcss/vite": "^4.1.16",
"@types/eslint-config-prettier": "^6.11.3",
"@types/lodash-es": "^4.17.12",
"eslint": "^9.36.0",
"@types/node": "^24.10.0",
"eslint": "^9.39.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4",
"prettier": "^3.6.2"
"prettier": "^3.6.2",
"tailwindcss": "^4.1.16",
"vue-tsc": "^3.1.3"
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ const logger = createConsola({

export default defineEventHandler(async event => {
const path = event.context.params?.path || [];
const targetUrl = `${import.meta.env.URL_API}/${Array.isArray(path) ? path.join('/') : path}`
const targetUrl = `${process.env.URL_API}/${Array.isArray(path) ? path.join('/') : path}`
.replaceAll('//', '/')
.replaceAll(':/', '://');

const method = event.method;

const query = getQuery(event);

const body = ['POST', 'PUT', 'PATCH'].includes(method) ? await readBody(event) : undefined;
const headers = event.headers;

const queryToSend = query.ignoreSerialize ? query : flattenObject(query);

try {
Expand All @@ -33,14 +30,18 @@ export default defineEventHandler(async event => {
headers: {
authorization: headers.get('Authorization') || '',
'x-forward-for': headers.get('x-forward-for') || '',
'Content-Type': headers.get('Content-Type') || 'application/json',
},
query: queryToSend,
});

const contentType = headers.get('Content-Type') || '';

const info = removeUndefinedKeys({
user: headers.get('user') || undefined,
body: removeUndefinedKeys(body),
body: processBodyForLog(body, headers),
query: removeUndefinedKeys(queryToSend),
...(contentType.includes('multipart/form-data') ? { contentType } : {}),
});

logger.success(
Expand All @@ -51,11 +52,14 @@ export default defineEventHandler(async event => {

return response;
} catch (err) {
const contentType = headers.get('Content-Type') || '';

const info = removeUndefinedKeys({
user: headers.get('user') || undefined,
body: removeUndefinedKeys(body),
body: processBodyForLog(body, headers),
query: removeUndefinedKeys(queryToSend),
response: normalizeFetchErrorServer(event, err) || undefined,
...(contentType.includes('multipart/form-data') ? { contentType } : {}),
});

logger.error(
Expand All @@ -68,6 +72,77 @@ export default defineEventHandler(async event => {
}
});

function processBodyForLog(body: any, headers: Headers): any {
if (!body) return body;

const contentType = headers.get('Content-Type') || '';

if (typeof body === 'string' && contentType.includes('multipart/form-data')) {
return parseMultipartForLog(body);
}

if (isPlainObject(body)) {
return sanitizeObjectBody(body);
}

return body;
}

function sanitizeObjectBody(obj: any): any {
if (!obj || typeof obj !== 'object') return obj;

if (isBinary(obj)) return '(binary)';

if (Array.isArray(obj)) {
return obj.map(item => (isBinary(item) ? '(binary)' : sanitizeObjectBody(item)));
}

const clean: Record<string, any> = {};
for (const key in obj) {
const value = obj[key];
clean[key] = isBinary(value) ? '(binary)' : sanitizeObjectBody(value);
}
return clean;
}

function isBinary(value: any): boolean {
if (!value) return false;
return (
value instanceof ArrayBuffer ||
value instanceof Uint8Array ||
(typeof Buffer !== 'undefined' && value instanceof Buffer) ||
(typeof File !== 'undefined' && value instanceof File) ||
(typeof Blob !== 'undefined' && value instanceof Blob)
);
}

function parseMultipartForLog(body: string): Record<string, string> {
const result: Record<string, string> = {};

const boundaryMatch = body.match(/^-+WebKitFormBoundary[^\r\n]+/m);
const boundary = boundaryMatch ? boundaryMatch[0] : null;
if (!boundary) return { raw: '(binary multipart/form-data)' };

const parts = body.split(boundary).filter(p => p.trim() && p.trim() !== '--');

for (const part of parts) {
const nameMatch = part.match(/name="([^"]+)"/);
if (!nameMatch || !nameMatch[1]) continue;

const fieldName: string = nameMatch[1];
const isFile = /filename="[^"]*"/.test(part);

if (isFile) {
result[fieldName] = '(binary)';
} else {
const valueMatch = part.match(/\r\n\r\n([\s\S]*?)\r\n$/);
result[fieldName] = valueMatch?.[1] ?? '';
}
}

return result;
}

function isJsonString(str: string): boolean {
try {
const parsed = JSON.parse(str);
Expand Down
File renamed without changes.
5 changes: 0 additions & 5 deletions src/app.vue

This file was deleted.

Loading
Loading