diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 4660078..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,11 +0,0 @@
-FROM oven/bun:latest
-
-WORKDIR /app
-COPY package.json package.json
-RUN bun install
-
-COPY . .
-RUN bun run build
-
-EXPOSE 3000
-CMD ["bun", "--bun", "build/index.js"]
\ No newline at end of file
diff --git a/app.Dockerfile b/app.Dockerfile
new file mode 100644
index 0000000..6da6e85
--- /dev/null
+++ b/app.Dockerfile
@@ -0,0 +1,34 @@
+FROM oven/bun:latest as builder
+
+WORKDIR /app
+
+# Copy package files
+COPY package.json bun.lockb ./
+
+# Install dependencies
+RUN bun install
+
+# Copy all source files
+COPY . .
+
+# Build the application
+RUN bun run build
+
+# Production stage
+FROM oven/bun:latest
+
+WORKDIR /app
+
+# Copy only the built files and necessary runtime files
+COPY --from=builder /app/build ./build
+COPY --from=builder /app/package.json ./package.json
+COPY --from=builder /app/bun.lockb ./bun.lockb
+
+# Install only production dependencies
+RUN bun install --production
+
+# Expose the port
+EXPOSE 3000
+
+# Start the application
+CMD ["bun", "build/index.js"]
\ No newline at end of file
diff --git a/cms/pb_migrations/1733582513_created_docs.js b/cms/pb_migrations/1733582513_created_docs.js
new file mode 100644
index 0000000..013ef9f
--- /dev/null
+++ b/cms/pb_migrations/1733582513_created_docs.js
@@ -0,0 +1,125 @@
+///
+migrate((db) => {
+ const collection = new Collection({
+ "id": "n9aqwacwoy4stzs",
+ "created": "2024-12-07 14:41:53.612Z",
+ "updated": "2024-12-07 14:41:53.612Z",
+ "name": "docs",
+ "type": "base",
+ "system": false,
+ "schema": [
+ {
+ "system": false,
+ "id": "r8barbx0",
+ "name": "slug",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "wjyjywhk",
+ "name": "title",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "wnxus5oj",
+ "name": "description",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "if2blvvp",
+ "name": "content",
+ "type": "editor",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "convertUrls": false
+ }
+ },
+ {
+ "system": false,
+ "id": "cypldga8",
+ "name": "tags",
+ "type": "select",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "maxSelect": 2,
+ "values": [
+ "self–hosting",
+ "docker",
+ "setup",
+ "fli.so"
+ ]
+ }
+ },
+ {
+ "system": false,
+ "id": "zwblvrwy",
+ "name": "author",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ },
+ {
+ "system": false,
+ "id": "xdwxo0if",
+ "name": "status",
+ "type": "bool",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {}
+ }
+ ],
+ "indexes": [],
+ "listRule": null,
+ "viewRule": null,
+ "createRule": null,
+ "updateRule": null,
+ "deleteRule": null,
+ "options": {}
+ });
+
+ return Dao(db).saveCollection(collection);
+}, (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs");
+
+ return dao.deleteCollection(collection);
+})
diff --git a/pocketbase/pb_migrations/1732955394_created_domains.js b/cms/pb_migrations/1733582837_created_authors.js
similarity index 61%
rename from pocketbase/pb_migrations/1732955394_created_domains.js
rename to cms/pb_migrations/1733582837_created_authors.js
index 3c1115d..103f6c6 100644
--- a/pocketbase/pb_migrations/1732955394_created_domains.js
+++ b/cms/pb_migrations/1733582837_created_authors.js
@@ -1,17 +1,17 @@
///
migrate((db) => {
const collection = new Collection({
- "id": "469ndywsep8ecvn",
- "created": "2024-11-30 08:29:54.301Z",
- "updated": "2024-11-30 08:29:54.301Z",
- "name": "domains",
+ "id": "bqnprvdsyv14cmi",
+ "created": "2024-12-07 14:47:17.289Z",
+ "updated": "2024-12-07 14:47:17.289Z",
+ "name": "authors",
"type": "base",
"system": false,
"schema": [
{
"system": false,
- "id": "vxevfkyy",
- "name": "domain",
+ "id": "pqx1adlk",
+ "name": "name",
"type": "text",
"required": false,
"presentable": false,
@@ -24,64 +24,57 @@ migrate((db) => {
},
{
"system": false,
- "id": "aeh0hbkr",
- "name": "user_id",
- "type": "relation",
+ "id": "q8bh7ydq",
+ "name": "email",
+ "type": "email",
"required": false,
"presentable": false,
"unique": false,
"options": {
- "collectionId": "_pb_users_auth_",
- "cascadeDelete": false,
- "minSelect": null,
- "maxSelect": 1,
- "displayFields": null
+ "exceptDomains": null,
+ "onlyDomains": null
}
},
{
"system": false,
- "id": "oxvonyoq",
- "name": "status",
- "type": "select",
+ "id": "zmyitrxf",
+ "name": "bio",
+ "type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
- "maxSelect": 1,
- "values": [
- "pending",
- "verified"
- ]
+ "min": null,
+ "max": null,
+ "pattern": ""
}
},
{
"system": false,
- "id": "vntyprgm",
- "name": "verification_token",
- "type": "text",
+ "id": "3ro1gcog",
+ "name": "avatar",
+ "type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
- "min": null,
- "max": null,
- "pattern": ""
+ "mimeTypes": [],
+ "thumbs": [],
+ "maxSelect": 1,
+ "maxSize": 5242880,
+ "protected": false
}
},
{
"system": false,
- "id": "dyvcs7x6",
- "name": "verification_method",
- "type": "select",
+ "id": "ezv96p4c",
+ "name": "social_links",
+ "type": "json",
"required": false,
"presentable": false,
"unique": false,
"options": {
- "maxSelect": 1,
- "values": [
- "dns",
- "file"
- ]
+ "maxSize": 2000000
}
}
],
@@ -97,7 +90,7 @@ migrate((db) => {
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
- const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn");
+ const collection = dao.findCollectionByNameOrId("bqnprvdsyv14cmi");
return dao.deleteCollection(collection);
})
diff --git a/cms/pb_migrations/1733583100_updated_users.js b/cms/pb_migrations/1733583100_updated_users.js
new file mode 100644
index 0000000..e2e8126
--- /dev/null
+++ b/cms/pb_migrations/1733583100_updated_users.js
@@ -0,0 +1,48 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
+
+ // add
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "uu943hxz",
+ "name": "bio",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ }))
+
+ // add
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "npn2vwkz",
+ "name": "social_links",
+ "type": "json",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "maxSize": 2000000
+ }
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
+
+ // remove
+ collection.schema.removeField("uu943hxz")
+
+ // remove
+ collection.schema.removeField("npn2vwkz")
+
+ return dao.saveCollection(collection)
+})
diff --git a/cms/pb_migrations/1733583162_deleted_authors.js b/cms/pb_migrations/1733583162_deleted_authors.js
new file mode 100644
index 0000000..60d3a85
--- /dev/null
+++ b/cms/pb_migrations/1733583162_deleted_authors.js
@@ -0,0 +1,96 @@
+///
+migrate((db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId("bqnprvdsyv14cmi");
+
+ return dao.deleteCollection(collection);
+}, (db) => {
+ const collection = new Collection({
+ "id": "bqnprvdsyv14cmi",
+ "created": "2024-12-07 14:47:17.289Z",
+ "updated": "2024-12-07 14:47:17.289Z",
+ "name": "authors",
+ "type": "base",
+ "system": false,
+ "schema": [
+ {
+ "system": false,
+ "id": "pqx1adlk",
+ "name": "name",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "q8bh7ydq",
+ "name": "email",
+ "type": "email",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "exceptDomains": null,
+ "onlyDomains": null
+ }
+ },
+ {
+ "system": false,
+ "id": "zmyitrxf",
+ "name": "bio",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "3ro1gcog",
+ "name": "avatar",
+ "type": "file",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "mimeTypes": [],
+ "thumbs": [],
+ "maxSelect": 1,
+ "maxSize": 5242880,
+ "protected": false
+ }
+ },
+ {
+ "system": false,
+ "id": "ezv96p4c",
+ "name": "social_links",
+ "type": "json",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "maxSize": 2000000
+ }
+ }
+ ],
+ "indexes": [],
+ "listRule": null,
+ "viewRule": null,
+ "createRule": null,
+ "updateRule": null,
+ "deleteRule": null,
+ "options": {}
+ });
+
+ return Dao(db).saveCollection(collection);
+})
diff --git a/cms/pb_migrations/1733583222_updated_docs.js b/cms/pb_migrations/1733583222_updated_docs.js
new file mode 100644
index 0000000..704b344
--- /dev/null
+++ b/cms/pb_migrations/1733583222_updated_docs.js
@@ -0,0 +1,36 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "xdwxo0if",
+ "name": "published",
+ "type": "bool",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {}
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "xdwxo0if",
+ "name": "status",
+ "type": "bool",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {}
+ }))
+
+ return dao.saveCollection(collection)
+})
diff --git a/cms/pb_migrations/1733583307_updated_docs.js b/cms/pb_migrations/1733583307_updated_docs.js
new file mode 100644
index 0000000..38ef326
--- /dev/null
+++ b/cms/pb_migrations/1733583307_updated_docs.js
@@ -0,0 +1,36 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs")
+
+ // add
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "slqrtrqd",
+ "name": "category",
+ "type": "select",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "maxSelect": 1,
+ "values": [
+ "Getting Started",
+ "Features",
+ "API Documentation",
+ "Troubleshooting",
+ "FAQs"
+ ]
+ }
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs")
+
+ // remove
+ collection.schema.removeField("slqrtrqd")
+
+ return dao.saveCollection(collection)
+})
diff --git a/cms/pb_migrations/1733584117_created_blogs.js b/cms/pb_migrations/1733584117_created_blogs.js
new file mode 100644
index 0000000..4cce92a
--- /dev/null
+++ b/cms/pb_migrations/1733584117_created_blogs.js
@@ -0,0 +1,157 @@
+///
+migrate((db) => {
+ const collection = new Collection({
+ "id": "3mjc7om79ldoiu5",
+ "created": "2024-12-07 15:08:37.620Z",
+ "updated": "2024-12-07 15:08:37.620Z",
+ "name": "blogs",
+ "type": "base",
+ "system": false,
+ "schema": [
+ {
+ "system": false,
+ "id": "cqlip1bv",
+ "name": "slug",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "7qakq1fc",
+ "name": "title",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "ibgca345",
+ "name": "description",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "lnxn4exu",
+ "name": "content",
+ "type": "editor",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "convertUrls": false
+ }
+ },
+ {
+ "system": false,
+ "id": "06crocuf",
+ "name": "tags",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "dkhjkerb",
+ "name": "author_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": null,
+ "displayFields": null
+ }
+ },
+ {
+ "system": false,
+ "id": "ib038pli",
+ "name": "status",
+ "type": "select",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "maxSelect": 1,
+ "values": [
+ "draft",
+ "published",
+ "archived"
+ ]
+ }
+ },
+ {
+ "system": false,
+ "id": "8mgw6t5l",
+ "name": "published_at",
+ "type": "date",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": "",
+ "max": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "ikxz28su",
+ "name": "featured_image",
+ "type": "file",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "mimeTypes": [],
+ "thumbs": [],
+ "maxSelect": 1,
+ "maxSize": 5242880,
+ "protected": false
+ }
+ }
+ ],
+ "indexes": [],
+ "listRule": null,
+ "viewRule": null,
+ "createRule": null,
+ "updateRule": null,
+ "deleteRule": null,
+ "options": {}
+ });
+
+ return Dao(db).saveCollection(collection);
+}, (db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId("3mjc7om79ldoiu5");
+
+ return dao.deleteCollection(collection);
+})
diff --git a/pocketbase/pb_migrations/1732803633_updated_subscriptions.js b/cms/pb_migrations/1733588991_updated_docs.js
similarity index 52%
rename from pocketbase/pb_migrations/1732803633_updated_subscriptions.js
rename to cms/pb_migrations/1733588991_updated_docs.js
index 339c7fd..c1362e1 100644
--- a/pocketbase/pb_migrations/1732803633_updated_subscriptions.js
+++ b/cms/pb_migrations/1733588991_updated_docs.js
@@ -1,16 +1,18 @@
///
migrate((db) => {
const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("4zjs84zpsai0wu4")
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs")
- collection.listRule = null
+ collection.listRule = ""
+ collection.viewRule = ""
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("4zjs84zpsai0wu4")
+ const collection = dao.findCollectionByNameOrId("n9aqwacwoy4stzs")
- collection.listRule = "user_id = @request.auth.id"
+ collection.listRule = null
+ collection.viewRule = null
return dao.saveCollection(collection)
})
diff --git a/cms/pocketbase b/cms/pocketbase
new file mode 100755
index 0000000..b44af64
Binary files /dev/null and b/cms/pocketbase differ
diff --git a/docker-compose.yml b/docker-compose.yml
index 2786c68..86519bc 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,26 +1,46 @@
version: "3.8"
+networks:
+ app_network:
+ driver: bridge
+
+volumes:
+ pb_data:
+ pb_public:
+ pb_hooks:
+ pb_migrations:
+ node_modules:
+
services:
pocketbase:
- image: ghcr.io/muchobien/pocketbase:0.22.27
container_name: pocketbase
+ build:
+ context: .
+ dockerfile: pocketbase.Dockerfile
restart: unless-stopped
ports:
- - "8090:8090"
+ - "8092:8092"
volumes:
- - ./pocketbase/pb_data:/pb_data
- - ./pocketbase/pb_public:/pb_public
- - ./pocketbase/pb_hooks:/pb_hooks
+ - pb_data:/pb/pb_data
+ - pb_public:/pb/pb_public
+ - pb_hooks:/pb/pb_hooks
+ - pb_migrations:/pb/pb_migrations
networks:
- app_network
+ healthcheck:
+ test:
+ ["CMD", "wget", "-q", "--spider", "http://localhost:8092/api/health"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
environment:
- - ORIGIN=http://localhost:3000 # Allow CORS requests from SvelteKit during development
+ - DISABLE_GZIP=true
- sveltekit-app:
+ app:
+ container_name: app
build:
context: .
- dockerfile: Dockerfile
- container_name: sveltekit-app
+ dockerfile: app.Dockerfile
ports:
- "3000:3000"
depends_on:
@@ -28,7 +48,7 @@ services:
env_file:
- .env
environment:
- - PUBLIC_POCKETBASE_URL=http://pocketbase:8090 # Use Docker network service name
+ - PUBLIC_POCKETBASE_URL=${PUBLIC_POCKETBASE_URL}
- PUBLIC_APPLICATION_NAME=${PUBLIC_APPLICATION_NAME}
- PUBLIC_APPLICATION_URL=${PUBLIC_APPLICATION_URL}
- ENCRYPTION=${ENCRYPTION}
@@ -43,13 +63,9 @@ services:
- STRIPE_ENTERPRISE_PRICE_ID=${STRIPE_ENTERPRISE_PRICE_ID}
- PUBLIC_SITE_URL=${PUBLIC_SITE_URL}
- STRIPE_PRODUCT_ID=${STRIPE_PRODUCT_ID}
- - ORIGIN=http://localhost:3000
+
networks:
- app_network
volumes:
- .:/app
- - /app/node_modules
-
-networks:
- app_network:
- driver: bridge
+ - node_modules:/app/node_modules
diff --git a/package.json b/package.json
index 46af6bd..b24a88c 100644
--- a/package.json
+++ b/package.json
@@ -34,11 +34,13 @@
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
- "pocketbase": "pocketbase/pocketbase serve",
- "pocketbase:dev": "pocketbase/pocketbase serve --http=127.0.0.1:8090 --dir=pocketbase/pocketbase/pb_data",
- "pocketbase:export": "pocketbase schema export pb_schema.json",
- "typegen": "pocketbase-typegen --db pocketbase/pb_data/data.db --out ./src/lib/types/generated-types.ts",
- "start": "bun run pocketbase & bun run dev",
+ "app:pocketbase": "pocketbase/pocketbase serve --http=127.0.0.1:8090 --dir=pocketbase/pb_data",
+ "cms:pocketbase": "cms/pocketbase serve --http=127.0.0.1:8091 --dir=cms/pb_data",
+ "app:pocketbase:export": "pocketbase/pocketbase schema export pb_schema.json",
+ "cms:pocketbase:export": "cms/pocketbase schema export pb_schema.json",
+ "app:typegen": "pocketbase-typegen --db pocketbase/pb_data/data.db --out ./src/lib/types/generated-types.ts",
+ "cms:typegen": "pocketbase-typegen --db cms/pb_data/data.db --out ./src/lib/types/cms-generated-types.ts",
+ "start": "bun run app:pocketbase & bun run dev",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:e2e": "playwright test",
diff --git a/pocketbase.Dockerfile b/pocketbase.Dockerfile
new file mode 100644
index 0000000..fd7dc09
--- /dev/null
+++ b/pocketbase.Dockerfile
@@ -0,0 +1,32 @@
+FROM alpine:latest
+
+ARG PB_VERSION=0.22.27
+
+# Install required tools
+RUN apk add --no-cache \
+ ca-certificates \
+ unzip \
+ wget \
+ bash
+
+# Download and unzip PocketBase
+ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
+RUN unzip /tmp/pb.zip -d /pb/
+
+# Create required directories
+WORKDIR /pb
+RUN mkdir -p /pb/pb_data /pb/pb_migrations /pb/pb_hooks
+
+# Copy migrations
+COPY ./pocketbase/pb_migrations /pb/pb_migrations/
+
+# Set correct permissions
+RUN chmod 755 /pb/pocketbase && \
+ chown -R nobody:nobody /pb
+
+USER nobody
+
+EXPOSE 8092
+
+# Start PocketBase with automigrate and CORS enabled
+CMD ["/pb/pocketbase", "serve", "--http=0.0.0.0:8092", "--automigrate"]
\ No newline at end of file
diff --git a/pocketbase/pb_migrations/1732803643_updated_subscriptions.js b/pocketbase/pb_migrations/1732803643_updated_subscriptions.js
deleted file mode 100644
index 50fe5e3..0000000
--- a/pocketbase/pb_migrations/1732803643_updated_subscriptions.js
+++ /dev/null
@@ -1,16 +0,0 @@
-///
-migrate((db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("4zjs84zpsai0wu4")
-
- collection.listRule = "user_id = @request.auth.id"
-
- return dao.saveCollection(collection)
-}, (db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("4zjs84zpsai0wu4")
-
- collection.listRule = null
-
- return dao.saveCollection(collection)
-})
diff --git a/pocketbase/pb_migrations/1732858379_updated_users.js b/pocketbase/pb_migrations/1732858379_updated_users.js
deleted file mode 100644
index 7801e74..0000000
--- a/pocketbase/pb_migrations/1732858379_updated_users.js
+++ /dev/null
@@ -1,36 +0,0 @@
-///
-migrate((db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
-
- collection.options = {
- "allowEmailAuth": true,
- "allowOAuth2Auth": true,
- "allowUsernameAuth": true,
- "exceptEmailDomains": null,
- "manageRule": null,
- "minPasswordLength": 8,
- "onlyEmailDomains": null,
- "onlyVerified": false,
- "requireEmail": false
- }
-
- return dao.saveCollection(collection)
-}, (db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("_pb_users_auth_")
-
- collection.options = {
- "allowEmailAuth": true,
- "allowOAuth2Auth": true,
- "allowUsernameAuth": true,
- "exceptEmailDomains": null,
- "manageRule": null,
- "minPasswordLength": 8,
- "onlyEmailDomains": null,
- "onlyVerified": true,
- "requireEmail": true
- }
-
- return dao.saveCollection(collection)
-})
diff --git a/pocketbase/pb_migrations/1732955580_updated_domains.js b/pocketbase/pb_migrations/1732955580_updated_domains.js
deleted file mode 100644
index fa6a259..0000000
--- a/pocketbase/pb_migrations/1732955580_updated_domains.js
+++ /dev/null
@@ -1,24 +0,0 @@
-///
-migrate((db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn")
-
- collection.listRule = "user_id = @request.auth.id"
- collection.viewRule = "user_id = @request.auth.id"
- collection.createRule = "user_id = @request.auth.id"
- collection.updateRule = "user_id = @request.auth.id"
- collection.deleteRule = "user_id = @request.auth.id"
-
- return dao.saveCollection(collection)
-}, (db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn")
-
- collection.listRule = null
- collection.viewRule = null
- collection.createRule = null
- collection.updateRule = null
- collection.deleteRule = null
-
- return dao.saveCollection(collection)
-})
diff --git a/pocketbase/pb_migrations/1732955602_updated_domains.js b/pocketbase/pb_migrations/1732955602_updated_domains.js
deleted file mode 100644
index 8a54781..0000000
--- a/pocketbase/pb_migrations/1732955602_updated_domains.js
+++ /dev/null
@@ -1,16 +0,0 @@
-///
-migrate((db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn")
-
- collection.createRule = "@request.auth.id != ''"
-
- return dao.saveCollection(collection)
-}, (db) => {
- const dao = new Dao(db)
- const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn")
-
- collection.createRule = "user_id = @request.auth.id"
-
- return dao.saveCollection(collection)
-})
diff --git a/pocketbase/pb_migrations/1732803670_updated_users.js b/pocketbase/pb_migrations/1733826839_updated_users.js
similarity index 100%
rename from pocketbase/pb_migrations/1732803670_updated_users.js
rename to pocketbase/pb_migrations/1733826839_updated_users.js
diff --git a/pocketbase/pb_migrations/1732961407_updated_urls.js b/pocketbase/pb_migrations/1733826874_updated_urls.js
similarity index 56%
rename from pocketbase/pb_migrations/1732961407_updated_urls.js
rename to pocketbase/pb_migrations/1733826874_updated_urls.js
index c90ebf9..ed527f1 100644
--- a/pocketbase/pb_migrations/1732961407_updated_urls.js
+++ b/pocketbase/pb_migrations/1733826874_updated_urls.js
@@ -3,18 +3,18 @@ migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("yq7y9q93v9mlxmq")
- // add
+ // update
collection.schema.addField(new SchemaField({
"system": false,
- "id": "ddtxaev3",
- "name": "domain_id",
+ "id": "oqmxf691",
+ "name": "created_by",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
- "collectionId": "469ndywsep8ecvn",
- "cascadeDelete": false,
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
@@ -26,8 +26,23 @@ migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("yq7y9q93v9mlxmq")
- // remove
- collection.schema.removeField("ddtxaev3")
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "oqmxf691",
+ "name": "created_by",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
return dao.saveCollection(collection)
})
diff --git a/pocketbase/pb_migrations/1733826887_updated_customers.js b/pocketbase/pb_migrations/1733826887_updated_customers.js
new file mode 100644
index 0000000..3270923
--- /dev/null
+++ b/pocketbase/pb_migrations/1733826887_updated_customers.js
@@ -0,0 +1,48 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("qxxr33ci9lj5ub1")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "n3mc4xme",
+ "name": "user_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": true,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("qxxr33ci9lj5ub1")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "n3mc4xme",
+ "name": "user_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+})
diff --git a/pocketbase/pb_migrations/1733826899_updated_domains.js b/pocketbase/pb_migrations/1733826899_updated_domains.js
new file mode 100644
index 0000000..f75d396
--- /dev/null
+++ b/pocketbase/pb_migrations/1733826899_updated_domains.js
@@ -0,0 +1,48 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "aeh0hbkr",
+ "name": "user_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": true,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("469ndywsep8ecvn")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "aeh0hbkr",
+ "name": "user_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+})
diff --git a/pocketbase/pb_migrations/1733826908_deleted_payment_methods.js b/pocketbase/pb_migrations/1733826908_deleted_payment_methods.js
new file mode 100644
index 0000000..35d434e
--- /dev/null
+++ b/pocketbase/pb_migrations/1733826908_deleted_payment_methods.js
@@ -0,0 +1,127 @@
+///
+migrate((db) => {
+ const dao = new Dao(db);
+ const collection = dao.findCollectionByNameOrId("x7wrm7o336mkewh");
+
+ return dao.deleteCollection(collection);
+}, (db) => {
+ const collection = new Collection({
+ "id": "x7wrm7o336mkewh",
+ "created": "2024-11-26 07:01:25.288Z",
+ "updated": "2024-11-29 05:32:59.439Z",
+ "name": "payment_methods",
+ "type": "base",
+ "system": false,
+ "schema": [
+ {
+ "system": false,
+ "id": "p4ryjmxg",
+ "name": "stripe_payment_method_id",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "rm365mrw",
+ "name": "card_brand",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "4eqxjol6",
+ "name": "card_last4",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "bjlj8suv",
+ "name": "card_exp_month",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "nvzibrot",
+ "name": "card_exp_year",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "5vihftrv",
+ "name": "is_default",
+ "type": "text",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "min": null,
+ "max": null,
+ "pattern": ""
+ }
+ },
+ {
+ "system": false,
+ "id": "a3air834",
+ "name": "customer_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "qxxr33ci9lj5ub1",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }
+ ],
+ "indexes": [],
+ "listRule": "customer_id.user_id = @request.auth.id",
+ "viewRule": "customer_id.user_id = @request.auth.id",
+ "createRule": "customer_id.user_id = @request.auth.id",
+ "updateRule": "customer_id.user_id = @request.auth.id",
+ "deleteRule": "customer_id.user_id = @request.auth.id",
+ "options": {}
+ });
+
+ return Dao(db).saveCollection(collection);
+})
diff --git a/pocketbase/pb_migrations/1733826918_updated_subscriptions.js b/pocketbase/pb_migrations/1733826918_updated_subscriptions.js
new file mode 100644
index 0000000..50b8ad0
--- /dev/null
+++ b/pocketbase/pb_migrations/1733826918_updated_subscriptions.js
@@ -0,0 +1,48 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("4zjs84zpsai0wu4")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "rs4t3bd8",
+ "name": "user_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": true,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("4zjs84zpsai0wu4")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "rs4t3bd8",
+ "name": "user_id",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+})
diff --git a/pocketbase/pb_migrations/1733826927_updated_tags.js b/pocketbase/pb_migrations/1733826927_updated_tags.js
new file mode 100644
index 0000000..5e8e1c8
--- /dev/null
+++ b/pocketbase/pb_migrations/1733826927_updated_tags.js
@@ -0,0 +1,48 @@
+///
+migrate((db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("6156foct15u4yk8")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "r1rj0tde",
+ "name": "created_by",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": true,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+}, (db) => {
+ const dao = new Dao(db)
+ const collection = dao.findCollectionByNameOrId("6156foct15u4yk8")
+
+ // update
+ collection.schema.addField(new SchemaField({
+ "system": false,
+ "id": "r1rj0tde",
+ "name": "created_by",
+ "type": "relation",
+ "required": false,
+ "presentable": false,
+ "unique": false,
+ "options": {
+ "collectionId": "_pb_users_auth_",
+ "cascadeDelete": false,
+ "minSelect": null,
+ "maxSelect": 1,
+ "displayFields": null
+ }
+ }))
+
+ return dao.saveCollection(collection)
+})
diff --git a/src/app.css b/src/app.css
index d1e7eb2..1644d68 100644
--- a/src/app.css
+++ b/src/app.css
@@ -71,6 +71,14 @@
--ring: 24 5.7% 82.9%;
}
+
+ html {
+ font-family: Satoshi, system-ui, sans-serif;
+ }
+
+ body {
+ @apply bg-background font-medium text-foreground;
+ }
}
@layer base {
@@ -80,4 +88,20 @@
body {
@apply bg-background text-foreground;
}
+
+ @font-face {
+ font-family: "Satoshi";
+ src: url("/fonts/satoshi/Satoshi-Variable.woff2") format("woff2");
+ font-weight: 300 900;
+ font-display: swap;
+ font-style: normal;
+ }
+
+ @font-face {
+ font-family: "Satoshi";
+ src: url("/fonts/satoshi/Satoshi-VariableItalic.woff2") format("woff2");
+ font-weight: 300 900;
+ font-display: swap;
+ font-style: italic;
+ }
}
diff --git a/src/app.d.ts b/src/app.d.ts
index 10fbbcf..255bffa 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -7,7 +7,7 @@ declare global {
namespace App {
// interface Error {}
interface Locals {
- pb: TypedPocketBase
+ pb: TypedPocketBase;
user: import("pocketbase").default["authStore"]["model"];
}
// interface PageData {}
diff --git a/src/hooks.client.ts b/src/hooks.client.ts
index 1429387..bc0abf7 100644
--- a/src/hooks.client.ts
+++ b/src/hooks.client.ts
@@ -1,12 +1,9 @@
import type { Handle, HandleClientError } from "@sveltejs/kit";
import PocketBase from "pocketbase";
-import { dev } from "$app/environment";
import { env } from "$env/dynamic/public";
// Create a singleton instance
-export const pbClient = new PocketBase(
- dev ? "http://localhost:8090" : env.PUBLIC_POCKETBASE_URL,
-);
+export const pbClient = new PocketBase(env.PUBLIC_POCKETBASE_URL);
export const handleError: HandleClientError = async ({ error }) => {
// Handle client-side errors here
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
index 8a11e8a..8b8bd26 100644
--- a/src/hooks.server.ts
+++ b/src/hooks.server.ts
@@ -2,7 +2,7 @@ export let requestIp: string;
import { type Handle } from "@sveltejs/kit";
import { dev } from "$app/environment";
-import { createInstance } from "$lib/pocketbase";
+import { createInstance } from "./lib/pocketbase";
export const handle: Handle = async ({ event, resolve }) => {
event.locals.pb = createInstance();
diff --git a/src/lib/pocketbase.ts b/src/lib/pocketbase.ts
index 9397e49..8e54c91 100644
--- a/src/lib/pocketbase.ts
+++ b/src/lib/pocketbase.ts
@@ -1,36 +1,33 @@
import { env } from "$env/dynamic/public";
import PocketBase, { ClientResponseError } from "pocketbase";
-import { dev } from "$app/environment";
import type { TypedPocketBase } from "./types";
export function createInstance() {
- return new PocketBase(
- dev ? "http://0.0.0.0:8090" : env.PUBLIC_POCKETBASE_URL,
- ) as TypedPocketBase;
+ return new PocketBase(env.PUBLIC_POCKETBASE_URL) as TypedPocketBase;
}
export const pb = createInstance() as TypedPocketBase;
export function handlePocketBaseError(error: unknown) {
- if (error instanceof ClientResponseError) {
- // Connection refused
- if (error.originalError?.cause?.message?.includes('ECONNREFUSED')) {
- console.error('Cannot connect to PocketBase server');
- return new Error('Database connection failed');
- }
-
- // Auto-cancelled request
- if (error.isAbort) {
- console.error('Request was auto-cancelled');
- return new Error('Request timeout');
- }
+ if (error instanceof ClientResponseError) {
+ // Connection refused
+ if (error.originalError?.cause?.message?.includes("ECONNREFUSED")) {
+ console.error("Cannot connect to PocketBase server");
+ return new Error("Database connection failed");
+ }
- // Other PocketBase errors
- console.error(`PocketBase error ${error.status}: ${error.message}`);
- return error;
+ // Auto-cancelled request
+ if (error.isAbort) {
+ console.error("Request was auto-cancelled");
+ return new Error("Request timeout");
}
- // Unknown errors
- console.error('Unknown error:', error);
- return new Error('An unexpected error occurred');
+ // Other PocketBase errors
+ console.error(`PocketBase error ${error.status}: ${error.message}`);
+ return error;
+ }
+
+ // Unknown errors
+ console.error("Unknown error:", error);
+ return new Error("An unexpected error occurred");
}
diff --git a/src/lib/server/stripe-utils.ts b/src/lib/server/stripe-utils.ts
index a14ad6b..9173da7 100644
--- a/src/lib/server/stripe-utils.ts
+++ b/src/lib/server/stripe-utils.ts
@@ -1,9 +1,11 @@
import { stripe } from "./stripe";
import type { UsersResponse } from "$lib/types";
import { env } from "$env/dynamic/private";
-import { createInstance } from "$lib/pocketbase";
-export async function createOrRetrieveStripeCustomer(user: UsersResponse) {
+export async function createOrRetrieveStripeCustomer(
+ user: UsersResponse,
+ locals: any,
+) {
try {
console.log(
"Attempting to create or retrieve Stripe customer for user:",
@@ -11,16 +13,14 @@ export async function createOrRetrieveStripeCustomer(user: UsersResponse) {
);
// Create new pocketbase client as admin
- const pb = createInstance();
-
// Authenticate as admin
- await pb.admins.authWithPassword(
+ await locals.pb.admins.authWithPassword(
env.POCKETBASE_ADMIN_EMAIL!,
env.POCKETBASE_ADMIN_PASSWORD!,
);
// Check if customer already exists in PocketBase
- const existingCustomer = await pb
+ const existingCustomer = await locals.pb
.collection("customers")
.getFirstListItem(`user_id="${user.id}"`)
.catch(() => {
@@ -45,7 +45,7 @@ export async function createOrRetrieveStripeCustomer(user: UsersResponse) {
console.log("Stripe customer created successfully:", stripeCustomer.id);
// Create customer record in PocketBase using authenticated client
- const customer = await pb.collection("customers").create({
+ const customer = await locals.pb.collection("customers").create({
user_id: user.id,
stripe_customer_id: stripeCustomer.id,
});
diff --git a/src/lib/types/cms-extented-types.ts b/src/lib/types/cms-extented-types.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/types/cms-generated-types.ts b/src/lib/types/cms-generated-types.ts
new file mode 100644
index 0000000..229da51
--- /dev/null
+++ b/src/lib/types/cms-generated-types.ts
@@ -0,0 +1,113 @@
+/**
+* This file was @generated using pocketbase-typegen
+*/
+
+import type PocketBase from 'pocketbase'
+import type { RecordService } from 'pocketbase'
+
+export enum Collections {
+ Blogs = "blogs",
+ Docs = "docs",
+ Users = "users",
+}
+
+// Alias types for improved usability
+export type IsoDateString = string
+export type RecordIdString = string
+export type HTMLString = string
+
+// System fields
+export type BaseSystemFields = {
+ id: RecordIdString
+ created: IsoDateString
+ updated: IsoDateString
+ collectionId: string
+ collectionName: Collections
+ expand?: T
+}
+
+export type AuthSystemFields = {
+ email: string
+ emailVisibility: boolean
+ username: string
+ verified: boolean
+} & BaseSystemFields
+
+// Record types for each collection
+
+export enum BlogsStatusOptions {
+ "draft" = "draft",
+ "published" = "published",
+ "archived" = "archived",
+}
+export type BlogsRecord = {
+ author_id?: RecordIdString[]
+ content?: HTMLString
+ description?: string
+ featured_image?: string
+ published_at?: IsoDateString
+ slug?: string
+ status?: BlogsStatusOptions
+ tags?: string
+ title?: string
+}
+
+export enum DocsTagsOptions {
+ "self–hosting" = "self–hosting",
+ "docker" = "docker",
+ "setup" = "setup",
+ "fli.so" = "fli.so",
+}
+
+export enum DocsCategoryOptions {
+ "Getting Started" = "Getting Started",
+ "Features" = "Features",
+ "API Documentation" = "API Documentation",
+ "Troubleshooting" = "Troubleshooting",
+ "FAQs" = "FAQs",
+}
+export type DocsRecord = {
+ author?: RecordIdString
+ category?: DocsCategoryOptions
+ content?: HTMLString
+ description?: string
+ published?: boolean
+ slug?: string
+ tags?: DocsTagsOptions[]
+ title?: string
+}
+
+export type UsersRecord = {
+ avatar?: string
+ bio?: string
+ name?: string
+ social_links?: null | Tsocial_links
+}
+
+// Response types include system fields and match responses from the PocketBase API
+export type BlogsResponse = Required & BaseSystemFields
+export type DocsResponse = Required & BaseSystemFields
+export type UsersResponse = Required> & AuthSystemFields
+
+// Types containing all Records and Responses, useful for creating typing helper functions
+
+export type CollectionRecords = {
+ blogs: BlogsRecord
+ docs: DocsRecord
+ users: UsersRecord
+}
+
+export type CollectionResponses = {
+ blogs: BlogsResponse
+ docs: DocsResponse
+ users: UsersResponse
+}
+
+// Type for usage with type asserted PocketBase instance
+// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
+
+export type TypedPocketBase = PocketBase & {
+ collection(idOrName: 'blogs'): RecordService
+ collection(idOrName: 'docs'): RecordService
+ collection(idOrName: 'users'): RecordService
+}
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
deleted file mode 100644
index 613f24a..0000000
--- a/src/routes/+page.server.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { PageServerLoad } from "./$types";
-
-export const load = (async () => {
- return {};
-}) satisfies PageServerLoad;
diff --git a/src/routes/[slug]/+page.server.ts b/src/routes/[slug]/+page.server.ts
index 2c8938a..2a3ee64 100644
--- a/src/routes/[slug]/+page.server.ts
+++ b/src/routes/[slug]/+page.server.ts
@@ -2,91 +2,107 @@ import { error, fail, redirect, type Actions } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types";
import { env } from "$env/dynamic/private";
import type { UrlsResponse } from "$lib/types";
-import { createInstance } from "$lib/pocketbase";
import { hashPassword } from "$lib/utils/hash-password";
+import { createInstance } from "../../lib/pocketbase";
const HASH_SECRET = env.HASH_SECRET || "your-fallback-secret-key";
-export const load: PageServerLoad = async ({ params }) => {
- // Authenticate as admin because we have api rules that prevent unauthenticated access
+export const load: PageServerLoad = async ({ params, locals }) => {
console.log("Starting load function with params:", params);
- const pb = createInstance();
- await pb.admins.authWithPassword(
- env.POCKETBASE_ADMIN_EMAIL!,
- env.POCKETBASE_ADMIN_PASSWORD!,
- );
- console.log("Successfully authenticated with PocketBase");
-
- if (!params.slug) {
- console.log("No slug provided in params");
- throw error(400, "Slug is required");
- }
- console.log("Attempting to fetch URL with slug:", params.slug);
- const url = await pb
- .collection("urls")
- .getFirstListItem(`slug = "${params.slug}"`)
- .catch(() => {
- console.log("URL not found for slug:", params.slug);
- return null;
+ try {
+ // Authenticate as admin
+ await locals.pb.admins
+ .authWithPassword(
+ env.POCKETBASE_ADMIN_EMAIL!,
+ env.POCKETBASE_ADMIN_PASSWORD!,
+ )
+ .catch((e) => {
+ console.error("Failed to authenticate as admin:", e);
+ throw error(500, "Failed to authenticate with PocketBase");
+ });
+
+ if (!params.slug) {
+ console.log("No slug provided in params");
+ throw error(400, "Slug is required");
+ }
+
+ if (params.slug.match(/\.(ico|png|jpg|jpeg|svg)$/i)) {
+ return { isStaticAsset: true };
+ }
+
+ const url = await locals.pb
+ .collection("urls")
+ .getFirstListItem(`slug = "${params.slug}"`)
+ .catch(() => {
+ console.log("URL not found for slug:", params.slug);
+ return null;
+ });
+
+ if (!url) {
+ throw error(404, "Not found");
+ }
+ console.log("Found URL:", url);
+
+ // Increment clicks
+ console.log("Incrementing clicks for URL ID:", url.id);
+ await locals.pb.collection("urls").update(url.id, {
+ clicks: url.clicks + 1,
});
- if (!url) {
- throw error(404, "Not found");
- }
- console.log("Found URL:", url);
-
- // Increment clicks
- console.log("Incrementing clicks for URL ID:", url.id);
- await pb.collection("urls").update(url.id, {
- clicks: url.clicks + 1,
- });
-
- // Check if the url is expired and redirect to expiration URL if needed
- if (url.expiration && new Date(url.expiration) < new Date()) {
- console.log("URL is expired. Expiration date:", url.expiration);
- if (url.expiration_url) {
- console.log("Redirecting to expiration URL:", url.expiration_url);
- throw redirect(302, url.expiration_url);
+ // Check if the url is expired and redirect to expiration URL if needed
+ if (url.expiration && new Date(url.expiration) < new Date()) {
+ console.log("URL is expired. Expiration date:", url.expiration);
+ if (url.expiration_url) {
+ console.log("Redirecting to expiration URL:", url.expiration_url);
+ throw redirect(302, url.expiration_url);
+ }
+ throw error(410, "This link has expired");
}
- throw error(410, "This link has expired");
- }
- // If URL has password
- if (url.password_hash) {
- console.log("URL is password protected");
- return {
- isProtected: true,
- url_id: url.id,
- };
- }
+ // If URL has password
+ if (url.password_hash) {
+ console.log("URL is password protected");
+ return {
+ isProtected: true,
+ url_id: url.id,
+ };
+ }
- // If URL has meta data, return for brief display
- if (url.meta_title || url.meta_description || url.meta_image_url) {
- console.log("URL has meta data, returning meta information");
- return {
- meta: {
- title: url.meta_title,
- description: url.meta_description || "not working",
- image: url.meta_image_url,
- url: url.url,
- },
- };
- }
+ // If URL has meta data, return for brief display
+ if (url.meta_title || url.meta_description || url.meta_image_url) {
+ console.log("URL has meta data, returning meta information");
+ return {
+ meta: {
+ title: url.meta_title,
+ description: url.meta_description || "not working",
+ image: url.meta_image_url,
+ url: url.url,
+ },
+ };
+ }
- // No meta or password - direct redirect
- console.log("Redirecting to URL:", url.url);
- throw redirect(302, url.url);
+ // No meta or password - direct redirect
+ console.log("Redirecting to URL:", url.url);
+ throw redirect(302, url.url);
+ } catch (e) {
+ console.error("Error in load function:", e);
+ throw error(500, "Failed to load URL");
+ }
};
export const actions: Actions = {
verify_password: async ({ request }) => {
- const pb = createInstance();
- await pb.admins.authWithPassword(
+ const pb = createInstance(env.PUBLIC_POCKETBASE_URL!);
+ const authdata = await pb.admins.authWithPassword(
env.POCKETBASE_ADMIN_EMAIL!,
env.POCKETBASE_ADMIN_PASSWORD!,
);
+ if (!authdata.record) {
+ throw error(401, "Unauthorized");
+ }
+
// Get form data from request
const formData = await request.formData();
const url_id = formData.get("url_id") as string;
diff --git a/src/routes/app/login/+page.server.ts b/src/routes/app/login/+page.server.ts
index c945f76..546f04a 100644
--- a/src/routes/app/login/+page.server.ts
+++ b/src/routes/app/login/+page.server.ts
@@ -34,13 +34,14 @@ export const actions: Actions = {
if (!authData.record.verified) {
// Clear auth store since we don't want unverified users to remain logged in
locals.pb.authStore.clear();
-
+
// Send another verification email
await locals.pb.collection("users").requestVerification(email);
-
+
return fail(403, {
- message: "Please verify your email address. A new verification email has been sent.",
- unverified: true
+ message:
+ "Please verify your email address. A new verification email has been sent.",
+ unverified: true,
});
}
} catch (err) {
diff --git a/static/fonts/satoshi/Satoshi-Variable.woff b/static/fonts/satoshi/Satoshi-Variable.woff
new file mode 100644
index 0000000..f8dcd1d
Binary files /dev/null and b/static/fonts/satoshi/Satoshi-Variable.woff differ
diff --git a/static/fonts/satoshi/Satoshi-Variable.woff2 b/static/fonts/satoshi/Satoshi-Variable.woff2
new file mode 100644
index 0000000..b00e833
Binary files /dev/null and b/static/fonts/satoshi/Satoshi-Variable.woff2 differ
diff --git a/static/fonts/satoshi/Satoshi-VariableItalic.woff b/static/fonts/satoshi/Satoshi-VariableItalic.woff
new file mode 100644
index 0000000..3fe029e
Binary files /dev/null and b/static/fonts/satoshi/Satoshi-VariableItalic.woff differ
diff --git a/static/fonts/satoshi/Satoshi-VariableItalic.woff2 b/static/fonts/satoshi/Satoshi-VariableItalic.woff2
new file mode 100644
index 0000000..e7ab3a0
Binary files /dev/null and b/static/fonts/satoshi/Satoshi-VariableItalic.woff2 differ
diff --git a/tailwind.config.ts b/tailwind.config.ts
index eac77dd..61ac924 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -63,7 +63,7 @@ const config: Config = {
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
- sans: [...fontFamily.sans],
+ sans: ["Satoshi", ...fontFamily.sans],
},
boxShadow: {
right: "24px 0px 20px 0px rgba(0, 0, 0, 0.04)",
diff --git a/tsconfig.json b/tsconfig.json
index f4d0a0e..e671b90 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,11 +9,11 @@
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
- "moduleResolution": "bundler"
+ "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
-}
+}
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index 8e08994..a71188b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -3,6 +3,12 @@ import { sveltekit } from "@sveltejs/kit/vite";
export default defineConfig({
plugins: [sveltekit()],
+ server: {
+ cors: {
+ origin: true,
+ credentials: true,
+ },
+ },
test: {
include: ["src/**/*.{test,spec}.{js,ts}"],
},