Compare commits

..

2 commits

Author SHA1 Message Date
Benjamin Palko
10e0c09f49 copy from hestia 2025-03-30 22:42:53 -04:00
Benjamin Palko
8e44793a5a add bun 2025-03-30 16:35:13 -04:00
34 changed files with 2566 additions and 0 deletions

14
.editorconfig Normal file
View file

@ -0,0 +1,14 @@
root = true
[*]
end_of_line = lf
insert_final_newline = false
[*.{js,ts}]
indent_style = tab
indent_size = 4
[{*.{yml,mjs,json}}]
indent_style = space
indent_size = 2

27
.env Normal file
View file

@ -0,0 +1,27 @@
NPM_TOKEN=
##########################
# APP VARIABLES #
##########################
APP_NAME="[template]"
##########################
# SECRETS #
##########################
SECRETS_PASSWORD=secret_do_not_commit_or_change_this_create_.env.local_instead
SECRETS_SALT=secret_do_not_commit_or_change_this_create_.env.local_instead
SECRETS_IV_POSITION=secret_do_not_commit_or_change_this_create_.env.local_instead
##########################
# DATABASE #
##########################
DB_USER=${APP_NAME}
DB_PASS=test123
DATABASE_URL="postgres://${APP_NAME}:test123@localhost:5432/${APP_NAME}"
DIRECT_URL="postgres://${APP_NAME}:test123@localhost:5432/${APP_NAME}"
##########################
# AUTHENTICATION #
##########################
PUBLIC_CLERK_PUBLISHABLE_KEY=secret_do_not_commit_or_change_this_create_.env.local_instead
CLERK_SECRET_KEY=secret_do_not_commit_or_change_this_create_.env.local_instead

46
.github/workflows/deploy.yml vendored Normal file
View file

@ -0,0 +1,46 @@
name: Deployment
on:
push:
branches:
- main
jobs:
deploy-app:
name: Deploy ${{ env.GITHUB_REPOSITORY }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_SERVER_URL }}
username: ${{ env.GITHUB_REPOSITORY_OWNER }}
password: ${{ secrets.NPM_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
tags: git.palko.ca/${{ env.GITHUB_REPOSITORY }}:latest
deploy-storybook:
name: Deploy ${{ env.GITHUB_REPOSITORY }}
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
registry: ${{ env.GITHUB_SERVER_URL }}
username: ${{ env.GITHUB_REPOSITORY_OWNER }}
password: ${{ secrets.NPM_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
push: true
file: Dockerfile.storybook
tags: git.palko.ca/${{ env.GITHUB_REPOSITORY }}-storybook:latest

27
.github/workflows/pr.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: PR Gate
on:
pull_request:
branches: ['*']
jobs:
checks:
name: Checks
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Bun Setup
uses: oven-sh/setup-bun@v2
with:
bun-version-file: '.tool-versions'
- name: Install
run: bun install
- name: Lint
run: bun lint
- name: Build
run: bun run build
- name: Svelte Check
run: bun check
- name: Prisma Check
run: bun prisma:validate
- name: Test
run: bun run test:unit

28
.gitignore vendored Normal file
View file

@ -0,0 +1,28 @@
test-results
node_modules
# Output
.output
.vercel
.netlify
.wrangler
/.svelte-kit
/build
# OS
.DS_Store
Thumbs.db
.idea
# Storybook
storybook-static
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
*/dev.db
*/dev.db-journal
# Env files
.env.*

6
.prettierignore Normal file
View file

@ -0,0 +1,6 @@
# Package Managers
package-lock.json
pnpm-lock.yaml
yarn.lock
*.md

21
.prettierrc Normal file
View file

@ -0,0 +1,21 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 100,
"endOfLine": "lf",
"arrowParens": "always",
"jsxSingleQuote": false,
"semi": true,
"quoteProps": "as-needed",
"tabWidth": 4,
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

17
.storybook/main.js Normal file
View file

@ -0,0 +1,17 @@
/** @type { import('@storybook/sveltekit').StorybookConfig } */
const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|ts|svelte)'],
addons: [
'@chromatic-com/storybook',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-styling-webpack',
'@storybook/addon-svelte-csf',
'@storybook/addon-themes',
],
framework: {
name: '@storybook/sveltekit',
options: {},
},
};
export default config;

3
.storybook/preview.css Normal file
View file

@ -0,0 +1,3 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';

28
.storybook/preview.js Normal file
View file

@ -0,0 +1,28 @@
import { withThemeByDataAttribute } from '@storybook/addon-themes';
import './preview.css';
/** @type { import('@storybook/svelte').Preview } */
const preview = {
tags: ['autodocs'],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
decorators: [
withThemeByDataAttribute({
themes: {
light: 'light',
dark: 'dark',
night: 'night',
},
defaultTheme: 'dark',
attributeName: 'data-theme',
}),
],
};
export default preview;

1
.tool-versions Normal file
View file

@ -0,0 +1 @@
bun 1.2.5

17
Dockerfile Normal file
View file

@ -0,0 +1,17 @@
FROM oven/bun:1.2-alpine AS build
WORKDIR /opt/build
COPY . .
RUN bun install --frozen-lockfile \
&& bun run build
FROM node:18-alpine3.21
WORKDIR /opt/app
COPY ./package.json ./package.json
COPY --from=build --chown=1000:1000 /opt/build/node_modules/ /opt/app/node_modules/
COPY --from=build --chown=1000:1000 /opt/build/build /opt/app
ENTRYPOINT [ "node", "index.js" ]

11
Dockerfile.storybook Normal file
View file

@ -0,0 +1,11 @@
FROM oven/bun:1.2-alpine AS build
WORKDIR /opt/build
COPY . .
RUN bun install --frozen-lockfile \
&& bun run build-storybook
FROM nginx:alpine3.21
COPY --from=build --chown=1000:1000 /opt/build/storybook-static /usr/share/nginx/html

1986
bun.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
name: $APP_NAME
services:
database:
image: 'postgres:12-alpine'
ports:
- '5432:5432'
environment:
POSTGRES_USER: $DB_USER
POSTGRES_PASSWORD: $DB_PASS

View file

@ -0,0 +1,3 @@
services:
wait:
image: dokku/wait

0
e2e/demo.test.ts Normal file
View file

55
eslint.config.js Normal file
View file

@ -0,0 +1,55 @@
import prettier from 'eslint-config-prettier';
import js from '@eslint/js';
import { includeIgnoreFile } from '@eslint/compat';
import svelte from 'eslint-plugin-svelte';
import globals from 'globals';
import { fileURLToPath } from 'node:url';
import ts from 'typescript-eslint';
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
export default ts.config(
includeIgnoreFile(gitignorePath),
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs['flat/recommended'],
prettier,
...svelte.configs['flat/prettier'],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
{
files: ['**/*.svelte'],
languageOptions: {
parserOptions: {
parser: ts.parser,
},
},
},
{
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'no-restricted-syntax': [
'error',
{
selector:
'CallExpression:matches([callee.object.object.name="prisma"], [callee.object.object.name="prismaTransactionClient"], [callee.object.object.name="transactionClient"]):matches([callee.property.name="findFirst"], [callee.property.name="findMany"], [callee.property.name="updateMany"], [callee.property.name="deleteMany"], [callee.property.name="count"], [callee.property.name="aggregate"], [callee.property.name="groupBy"]):not(:has(ObjectExpression > Property[key.name="where"] > ObjectExpression > Property[key.name="tenantId"]))',
message:
'Please filter on the current tenant when using findFirst, findMany, updateMany, deleteMany, count, aggregate or groupBy.',
},
],
},
}
);

3
messages/en.json Normal file
View file

@ -0,0 +1,3 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format"
}

98
package.json Normal file
View file

@ -0,0 +1,98 @@
{
"name": "hestia",
"version": "0.0.1",
"type": "module",
"scripts": {
"postinstall": "prisma generate",
"dev": "bun database:up && bun prisma:generate && vite dev",
"build": "vite build",
"build-storybook": "storybook build",
"database:up": "docker compose -p hestia -f devops/docker-compose.dev.yml up -d && docker compose -p hestia -f devops/docker-compose.dev.yml -f devops/docker-compose.wait.yml run --rm wait -c hestia-database:5432 && bun prisma:push",
"database:down": "docker compose -p hestia -f devops/docker-compose.dev.yml down",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
"generate-secret": "bun ./scripts/generate-secret.ts",
"lint": "prettier --check . && eslint .",
"test:unit": "vitest",
"test": "bun run test:unit -- --run && bun run test:e2e",
"storybook": "storybook dev -p 6006",
"test:e2e": "playwright test",
"prisma:dev": "prisma migrate dev",
"prisma:format": "prisma format",
"prisma:generate": "prisma generate",
"prisma:push": "prisma db push",
"prisma:reset": "prisma migrate reset --force",
"prisma:studio": "prisma studio",
"prisma:validate": "prisma validate",
"prepare": "husky"
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.2",
"@eslint/compat": "^1.2.3",
"@playwright/test": "^1.45.3",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-svelte-csf": "^5.0.0-next.13",
"@storybook/addon-themes": "^8.5.0",
"@storybook/blocks": "^8.5.0",
"@storybook/svelte": "^8.5.0",
"@storybook/sveltekit": "^8.5.0",
"@storybook/test": "^8.5.0",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.15.3",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
"@types/bun": "^1.1.15",
"autoprefixer": "^10.4.20",
"daisyui": "^4.12.22",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
"globals": "^15.0.0",
"husky": "^9.1.7",
"lint-staged": "^15.3.0",
"prettier": "^3.3.2",
"prettier-plugin-svelte": "^3.2.6",
"prettier-plugin-tailwindcss": "^0.6.5",
"prisma": "6.3.1",
"storybook": "^8.5.0",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.9",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"vite": "^6.0.0",
"vitest": "^2.0.4"
},
"dependencies": {
"@atlas/svelte": "^1.0.5-alpha",
"@clerk/backend": "1.21.4",
"@clerk/themes": "^2.2.3",
"@inlang/paraglide-sveltekit": "^0.15.0",
"@pothos/core": "^4.3.0",
"@pothos/plugin-prisma": "^4.4.0",
"@prisma/client": "6.3.1",
"@tailwindcss/typography": "^0.5.15",
"clerk-sveltekit": "https://pkg.pr.new/wobsoriano/clerk-sveltekit@ca15d4e",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"eslint_d": "^14.3.0",
"graphql": "^16.9.0",
"graphql-yoga": "^5.10.4",
"lucide-svelte": "^0.469.0",
"pino": "^9.5.0",
"pino-pretty": "^13.0.0",
"tailwind-merge": "^2.5.5",
"twilio": "^5.4.0",
"zod": "^3.24.0"
},
"lint-staged": {
"*.{json,yml,yaml,css}": "prettier --write",
"*.{js,ts,svelte}": [
"prettier --write",
"eslint --fix"
]
}
}

0
playwright.config.ts Normal file
View file

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

13
prisma/schema.prisma Normal file
View file

@ -0,0 +1,13 @@
generator client {
provider = "prisma-client-js"
}
generator pothos {
provider = "prisma-pothos-types"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}

1
project.inlang/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
cache

View file

@ -0,0 +1,17 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-valid-js-identifier@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@2/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@0/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{languageTag}.json"
},
"locales": ["en"],
"baseLocale": "en"
}

0
src/lib/index.ts Normal file
View file

20
src/routes/+error.svelte Normal file
View file

@ -0,0 +1,20 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { messages } from '$lib/i18n';
</script>
<main class="flex h-full w-full flex-col items-center justify-center gap-8">
<div class="flex items-center gap-2">
<h1 class="text-5xl font-bold tracking-widest text-white">
{page.status}
</h1>
<div class="divider divider-horizontal"></div>
<h1 class="text-2xl font-bold">
{page.error?.message}
</h1>
</div>
<button onclick={() => goto('/')} class="btn btn-outline btn-neutral btn-wide"
>{messages.error_page_go_home()}</button
>
</main>

23
src/routes/+layout.svelte Normal file
View file

@ -0,0 +1,23 @@
<script lang="ts">
import ClerkProvider from 'clerk-sveltekit/client/ClerkProvider.svelte';
import { i18n } from '$lib/i18n';
import { ParaglideJS } from '@inlang/paraglide-sveltekit';
import '../app.css';
import { PUBLIC_CLERK_PUBLISHABLE_KEY } from '$env/static/public';
let { children } = $props();
</script>
<ParaglideJS {i18n}>
<ClerkProvider publishableKey={PUBLIC_CLERK_PUBLISHABLE_KEY}>
<div class="layout">
{@render children()}
</div>
</ClerkProvider>
</ParaglideJS>
<style>
.layout {
@apply h-screen w-screen p-8;
}
</style>

0
src/routes/+page.svelte Normal file
View file

BIN
static/favicon.png Normal file

Binary file not shown.

21
svelte.config.js Normal file
View file

@ -0,0 +1,21 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
version: {
name: '1.0.0-alpha',
},
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
},
};
export default config;

29
tailwind.config.ts Normal file
View file

@ -0,0 +1,29 @@
import typography from '@tailwindcss/typography';
import daisyui from 'daisyui';
import type { Config } from 'tailwindcss';
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
fontFamily: {
display: ['Baskervville SC'],
},
animation: {
fade: 'fadeIn .5s ease-in-out',
},
keyframes: {
fadeIn: {
from: { opacity: '0' },
to: { opacity: '1' },
},
},
},
},
plugins: [typography, daisyui],
daisyui: {
logs: false,
},
} satisfies Config;

19
tsconfig.json Normal file
View file

@ -0,0 +1,19 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

17
vite.config.ts Normal file
View file

@ -0,0 +1,17 @@
import { defineConfig } from 'vitest/config';
import { paraglide } from '@inlang/paraglide-sveltekit/vite';
import { sveltekit } from '@sveltejs/kit/vite';
export default defineConfig({
plugins: [
sveltekit(),
paraglide({
project: './project.inlang',
outdir: './src/lib/paraglide',
}),
],
test: {
include: ['src/**/*.{test,spec}.{js,ts}'],
},
});