project setup
This commit is contained in:
3
.env.example
Normal file
3
.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Drizzle
|
||||||
|
DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?ssl={"rejectUnauthorized":true}'
|
||||||
|
|
||||||
39
.eslintrc.cjs
Normal file
39
.eslintrc.cjs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/** @type {import("eslint").Linter.Config} */
|
||||||
|
const config = {
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
parserOptions: {
|
||||||
|
project: true,
|
||||||
|
},
|
||||||
|
plugins: ["@typescript-eslint"],
|
||||||
|
extends: [
|
||||||
|
"next/core-web-vitals",
|
||||||
|
"plugin:@typescript-eslint/recommended-type-checked",
|
||||||
|
"plugin:@typescript-eslint/stylistic-type-checked",
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
// These opinionated rules are enabled in stylistic-type-checked above.
|
||||||
|
// Feel free to reconfigure them to your own preference.
|
||||||
|
"@typescript-eslint/array-type": "off",
|
||||||
|
"@typescript-eslint/consistent-type-definitions": "off",
|
||||||
|
|
||||||
|
"@typescript-eslint/consistent-type-imports": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
prefer: "type-imports",
|
||||||
|
fixStyle: "inline-type-imports",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
|
||||||
|
"@typescript-eslint/require-await": "off",
|
||||||
|
"@typescript-eslint/no-misused-promises": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
checksVoidReturn: { attributes: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# Database
|
||||||
|
/prisma/db.sqlite
|
||||||
|
/prisma/db.sqlite-journal
|
||||||
|
|
||||||
|
# Next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
next-env.d.ts
|
||||||
|
|
||||||
|
# Production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Local env files
|
||||||
|
.env
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# Vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# Typescript
|
||||||
|
*.tsbuildinfo
|
||||||
18
.prettierrc.cjs
Normal file
18
.prettierrc.cjs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/** @type {import("prettier").Options & import("prettier-plugin-tailwindcss").PluginOptions} */
|
||||||
|
const config = {
|
||||||
|
plugins: ["prettier-plugin-tailwindcss"],
|
||||||
|
arrowParens: "avoid",
|
||||||
|
bracketSpacing: true,
|
||||||
|
quoteProps: "consistent",
|
||||||
|
jsxSingleQuote: false,
|
||||||
|
printWidth: 80,
|
||||||
|
semi: true,
|
||||||
|
singleQuote: false,
|
||||||
|
tabWidth: 2,
|
||||||
|
trailingComma: "all",
|
||||||
|
useTabs: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
16
drizzle.config.ts
Normal file
16
drizzle.config.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
import { type Config } from "drizzle-kit";
|
||||||
|
|
||||||
|
import { env } from "~/env";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
schema: "./src/server/db/schema.ts",
|
||||||
|
driver: "mysql2",
|
||||||
|
dbCredentials: {
|
||||||
|
uri: env.DATABASE_URL,
|
||||||
|
},
|
||||||
|
tablesFilter: ["me_*"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
15
next.config.js
Normal file
15
next.config.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
await import("./src/env.js");
|
||||||
|
|
||||||
|
/** @type {import("next").NextConfig} */
|
||||||
|
const config = {
|
||||||
|
reactStrictMode: true,
|
||||||
|
|
||||||
|
i18n: {
|
||||||
|
locales: ["en"],
|
||||||
|
defaultLocale: "en",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
52
package.json
Normal file
52
package.json
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "pihkaal.me",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "next build",
|
||||||
|
"db:push": "drizzle-kit push:mysql",
|
||||||
|
"db:studio": "drizzle-kit studio",
|
||||||
|
"dev": "next dev",
|
||||||
|
"lint": "next lint --cache --fix",
|
||||||
|
"start": "next start",
|
||||||
|
"format": "prettier --cache --write '**/*.{md,json,css,scss,js,mjs,cjs,ts,tsx}'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@planetscale/database": "^1.11.0",
|
||||||
|
"@t3-oss/env-nextjs": "^0.7.1",
|
||||||
|
"@tanstack/react-query": "^4.36.1",
|
||||||
|
"@trpc/client": "^10.43.6",
|
||||||
|
"@trpc/next": "^10.43.6",
|
||||||
|
"@trpc/react-query": "^10.43.6",
|
||||||
|
"@trpc/server": "^10.43.6",
|
||||||
|
"drizzle-orm": "^0.29.3",
|
||||||
|
"next": "^14.0.4",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-dom": "18.2.0",
|
||||||
|
"superjson": "^2.2.1",
|
||||||
|
"zod": "^3.22.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/eslint": "^8.44.7",
|
||||||
|
"@types/node": "^18.17.0",
|
||||||
|
"@types/react": "^18.2.37",
|
||||||
|
"@types/react-dom": "^18.2.15",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||||
|
"@typescript-eslint/parser": "^6.11.0",
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
|
"drizzle-kit": "^0.20.9",
|
||||||
|
"eslint": "^8.54.0",
|
||||||
|
"eslint-config-next": "^14.0.4",
|
||||||
|
"mysql2": "^3.6.1",
|
||||||
|
"postcss": "^8.4.31",
|
||||||
|
"prettier": "^3.1.0",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||||
|
"tailwindcss": "^3.3.5",
|
||||||
|
"typescript": "^5.1.6"
|
||||||
|
},
|
||||||
|
"ct3aMetadata": {
|
||||||
|
"initVersion": "7.25.1"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@8.14.1"
|
||||||
|
}
|
||||||
3967
pnpm-lock.yaml
generated
Normal file
3967
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
postcss.config.cjs
Normal file
10
postcss.config.cjs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
26
src/env.js
Normal file
26
src/env.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { createEnv } from "@t3-oss/env-nextjs";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const env = createEnv({
|
||||||
|
server: {
|
||||||
|
DATABASE_URL: z
|
||||||
|
.string()
|
||||||
|
.url()
|
||||||
|
.refine(
|
||||||
|
str => !str.includes("YOUR_MYSQL_URL_HERE"),
|
||||||
|
"You forgot to change the default URL",
|
||||||
|
),
|
||||||
|
NODE_ENV: z
|
||||||
|
.enum(["development", "test", "production"])
|
||||||
|
.default("development"),
|
||||||
|
},
|
||||||
|
|
||||||
|
client: {},
|
||||||
|
|
||||||
|
runtimeEnv: {
|
||||||
|
DATABASE_URL: process.env.DATABASE_URL,
|
||||||
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
|
},
|
||||||
|
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||||
|
emptyStringAsUndefined: true,
|
||||||
|
});
|
||||||
11
src/pages/_app.tsx
Normal file
11
src/pages/_app.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { type AppType } from "next/app";
|
||||||
|
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
|
||||||
|
import "~/styles/globals.css";
|
||||||
|
|
||||||
|
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||||
|
return <Component {...pageProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default api.withTRPC(MyApp);
|
||||||
18
src/pages/api/trpc/[trpc].ts
Normal file
18
src/pages/api/trpc/[trpc].ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createNextApiHandler } from "@trpc/server/adapters/next";
|
||||||
|
|
||||||
|
import { env } from "~/env";
|
||||||
|
import { appRouter } from "~/server/api/root";
|
||||||
|
import { createTRPCContext } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
export default createNextApiHandler({
|
||||||
|
router: appRouter,
|
||||||
|
createContext: createTRPCContext,
|
||||||
|
onError:
|
||||||
|
env.NODE_ENV === "development"
|
||||||
|
? ({ path, error }) => {
|
||||||
|
console.error(
|
||||||
|
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
12
src/pages/index.tsx
Normal file
12
src/pages/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import Head from "next/head";
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>pihkaal</title>
|
||||||
|
</Head>
|
||||||
|
<main></main>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
5
src/server/api/root.ts
Normal file
5
src/server/api/root.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { createTRPCRouter } from "~/server/api/trpc";
|
||||||
|
|
||||||
|
export const appRouter = createTRPCRouter({});
|
||||||
|
|
||||||
|
export type AppRouter = typeof appRouter;
|
||||||
48
src/server/api/trpc.ts
Normal file
48
src/server/api/trpc.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { initTRPC } from "@trpc/server";
|
||||||
|
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||||
|
import superjson from "superjson";
|
||||||
|
import { ZodError } from "zod";
|
||||||
|
|
||||||
|
import { db } from "~/server/db";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. CONTEXT
|
||||||
|
*/
|
||||||
|
|
||||||
|
type CreateContextOptions = Record<string, never>;
|
||||||
|
|
||||||
|
const createInnerTRPCContext = (_opts: CreateContextOptions) => {
|
||||||
|
return {
|
||||||
|
db,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createTRPCContext = (_opts: CreateNextContextOptions) => {
|
||||||
|
return createInnerTRPCContext({});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2. INITIALIZATION
|
||||||
|
*/
|
||||||
|
|
||||||
|
const t = initTRPC.context<typeof createTRPCContext>().create({
|
||||||
|
transformer: superjson,
|
||||||
|
errorFormatter({ shape, error }) {
|
||||||
|
return {
|
||||||
|
...shape,
|
||||||
|
data: {
|
||||||
|
...shape.data,
|
||||||
|
zodError:
|
||||||
|
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const createTRPCRouter = t.router;
|
||||||
|
|
||||||
|
export const publicProcedure = t.procedure;
|
||||||
12
src/server/db/index.ts
Normal file
12
src/server/db/index.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Client } from "@planetscale/database";
|
||||||
|
import { drizzle } from "drizzle-orm/planetscale-serverless";
|
||||||
|
|
||||||
|
import { env } from "~/env";
|
||||||
|
import * as schema from "./schema";
|
||||||
|
|
||||||
|
export const db = drizzle(
|
||||||
|
new Client({
|
||||||
|
url: env.DATABASE_URL,
|
||||||
|
}).connection(),
|
||||||
|
{ schema },
|
||||||
|
);
|
||||||
3
src/server/db/schema.ts
Normal file
3
src/server/db/schema.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { mysqlTableCreator } from "drizzle-orm/mysql-core";
|
||||||
|
|
||||||
|
export const mysqlTable = mysqlTableCreator(name => `me_${name}`);
|
||||||
3
src/styles/globals.css
Normal file
3
src/styles/globals.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
36
src/utils/api.ts
Normal file
36
src/utils/api.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { httpBatchLink, loggerLink } from "@trpc/client";
|
||||||
|
import { createTRPCNext } from "@trpc/next";
|
||||||
|
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
|
||||||
|
import superjson from "superjson";
|
||||||
|
|
||||||
|
import { type AppRouter } from "~/server/api/root";
|
||||||
|
|
||||||
|
const getBaseUrl = () => {
|
||||||
|
if (typeof window !== "undefined") return ""; // browser should use relative url
|
||||||
|
if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
|
||||||
|
return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
|
||||||
|
};
|
||||||
|
|
||||||
|
export const api = createTRPCNext<AppRouter>({
|
||||||
|
config() {
|
||||||
|
return {
|
||||||
|
transformer: superjson,
|
||||||
|
|
||||||
|
links: [
|
||||||
|
loggerLink({
|
||||||
|
enabled: opts =>
|
||||||
|
process.env.NODE_ENV === "development" ||
|
||||||
|
(opts.direction === "down" && opts.result instanceof Error),
|
||||||
|
}),
|
||||||
|
httpBatchLink({
|
||||||
|
url: `${getBaseUrl()}/api/trpc`,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type RouterInputs = inferRouterInputs<AppRouter>;
|
||||||
|
|
||||||
|
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
||||||
16
tailwind.config.ts
Normal file
16
tailwind.config.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { type Config } from "tailwindcss";
|
||||||
|
import { fontFamily } from "tailwindcss/defaultTheme";
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
content: ["./src/**/*.tsx"],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
fontFamily: {
|
||||||
|
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
42
tsconfig.json
Normal file
42
tsconfig.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Base Options: */
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"target": "es2022",
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"isolatedModules": true,
|
||||||
|
|
||||||
|
/* Strictness */
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"checkJs": true,
|
||||||
|
|
||||||
|
/* Bundled projects */
|
||||||
|
"lib": ["dom", "dom.iterable", "ES2022"],
|
||||||
|
"noEmit": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"plugins": [{ "name": "next" }],
|
||||||
|
"incremental": true,
|
||||||
|
|
||||||
|
/* Path Aliases */
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
".eslintrc.cjs",
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
"**/*.cjs",
|
||||||
|
"**/*.js",
|
||||||
|
".next/types/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user