├── .env ├── .eslintignore ├── .eslintrc.cjs ├── .github └── FUNDING.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── bun.lockb ├── db ├── schema.surql └── surreal.db │ ├── 000009.log │ ├── 000013.sst │ ├── CURRENT │ ├── IDENTITY │ ├── LOCK │ ├── LOG │ ├── LOG.old.1698801593622438 │ ├── MANIFEST-000010 │ ├── OPTIONS-000007 │ └── OPTIONS-000012 ├── docker-compose.yml ├── master.css.ts ├── package.json ├── src ├── app.d.ts ├── app.html ├── hooks.server.ts ├── index.test.ts ├── lib │ ├── createStore.ts │ ├── logout.ts │ ├── modules │ │ ├── Footer.svelte │ │ └── Nav.svelte │ ├── styles │ │ ├── button.mcss.ts │ │ ├── card.mcss.ts │ │ └── form.mcss.ts │ ├── surreal.ts │ └── types │ │ ├── Post.type.ts │ │ ├── User.type.ts │ │ └── index.ts ├── master.css.ts └── routes │ ├── (user) │ ├── +page.server.ts │ ├── +page.svelte │ ├── author │ │ └── [author_slug] │ │ │ ├── +page.server.ts │ │ │ └── +page.svelte │ └── post │ │ ├── [post_slug] │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ ├── edit │ │ │ ├── +page.server.ts │ │ │ ├── +page.svelte │ │ │ └── schema.ts │ │ └── schema.ts │ │ └── add │ │ ├── +page.server.ts │ │ ├── +page.svelte │ │ └── schema.ts │ ├── +layout.server.ts │ ├── +layout.svelte │ ├── login │ ├── +page.server.ts │ ├── +page.svelte │ └── schema.ts │ └── register │ ├── +page.server.ts │ ├── +page.svelte │ └── schema.ts ├── static ├── favicon.ico └── favicon.png ├── svelte.config.js ├── tsconfig.json └── vite.config.ts /.env: -------------------------------------------------------------------------------- 1 | PUBLIC_SURREALDB_URL=http://localhost:8000/rpc 2 | DB_USER=SurrealSvelteKit 3 | DB_PASSWORD=*****_*** # Whoever understands, understands 😵 4 | DB_LOG_LEVEL=info # Can be one of [error, warn, info, debug, trace, full] 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier' 8 | ], 9 | parser: '@typescript-eslint/parser', 10 | plugins: ['@typescript-eslint'], 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | extraFileExtensions: ['.svelte'] 15 | }, 16 | env: { 17 | browser: true, 18 | es2017: true, 19 | node: true 20 | }, 21 | overrides: [ 22 | { 23 | files: ['*.svelte'], 24 | parser: 'svelte-eslint-parser', 25 | parserOptions: { 26 | parser: '@typescript-eslint/parser' 27 | } 28 | } 29 | ] 30 | }; 31 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [oskar-gmerek] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env.* 7 | !.env.example 8 | vite.config.js.timestamp-* 9 | vite.config.ts.timestamp-* 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.quickSuggestions": { 3 | "strings": true 4 | } 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2023 [Oskar Gmerek](oskargmerek.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Surreal Sveltekit: A Starter Kit with SurrealDB and SvelteKit, featuring Authentication and CRUD Operations + Realtime 2 | 3 | ## Feedback 4 | ⭐ Found this project helpful? Show some love with a star and consider to be a [SPONSOR](https://github.com/sponsors/oskar-gmerek)! Your support keeps the code evolving. 🚀 5 | 6 | ## Free Database 7 | ✨ Grab [FREE SurrealDB Cloud instance](https://surrealist.app/referral?code=4pn5aba943lpbn8l) 🚀🚀🚀 8 | 9 | ## Auth 10 | 11 | https://github.com/oskar-gmerek/surreal-sveltekit/assets/53402105/e6df634b-e062-4154-b153-71a0c0735c3a 12 | 13 | ### Username Validation 14 | 15 | https://github.com/oskar-gmerek/surreal-sveltekit/assets/53402105/8ef24ab4-672e-4669-bd26-f7580523907a 16 | 17 | ## CRUD & Realtime 18 | 19 | https://github.com/oskar-gmerek/surreal-sveltekit/assets/53402105/30232b45-a5a9-4368-bc0b-095c11b6ef5e 20 | 21 | ## Requirements: 22 | - [Docker](https://www.docker.com/products/docker-desktop) 23 | - Ensure that ports `5173` and `8000` are available for use. 24 | 25 | ## Modern Stack: 26 | - `SurrealDB` - The ultimate multi-model database 27 | - `surrealdb.js` for interacting with awesome [SurrealDB](https://surrealdb.com) 28 | - `sveltekit-superforms` + `zod` to enable super powers in working with forms - [Superforms](https://superforms.rocks/) | [Zod](https://zod.dev/) 29 | - `@master/css.svelte` as an interesting alternative for TailwindCSS - [MasterCSS](https://beta.css.master.co/docs/installation) 30 | - `dayjs` to enable high level of DX in working with time and dates - [Day.js](https://day.js.org/) 31 | 32 | 33 | ## Setup Process: 34 | 35 | 1. Clone the repository to your local machine. 36 | 2. Install dependencies. For example: 37 | ```ts 38 | bun install 39 | ``` 40 | If you don't have [bun](https://bun.sh) installed, you can utilize pnpm or any other Node.js package manager. For instance: 41 | ``` 42 | pnpm install 43 | ``` 44 | 3. Execute the provided npm script. For example: 45 | ```ts 46 | bun run multitaskum:developum 47 | ``` 48 | If you don't have [bun](https://bun.sh) installed, you can utilize pnpm or any other Node.js package manager. For instance: 49 | ```ts 50 | pnpm run multitaskum:developum 51 | ``` 52 | This command will initiate SurrealDB within a Docker container and launch SvelteKit in your local development environment. 53 | 54 | 4. Access [http://localhost:5173](http://localhost:5173) using your preferred web browser. 55 | 5. Test various functionalities such as creating a new account, logging in, generating new posts, editing posts, and deleting them. 56 | 57 | ## Issues and Contributions: 58 | 59 | - If you encounter any issues (as I rushed a bit to meet the hacktoberfest deadline), please feel free to [open an issue](https://github.com/oskar-gmerek/surreal-sveltekit/issues). 60 | - I am open to any contributions as I aspire to make this starter a top choice for initiating projects that utilize the best modern technologies. 61 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskar-gmerek/surreal-sveltekit/be60e3a4e47cc5f0b2ad8c8deceec71c7c9dd440/bun.lockb -------------------------------------------------------------------------------- /db/schema.surql: -------------------------------------------------------------------------------- 1 | 2 | -- ------------------------------ 3 | -- -- Export generated by Surrealist on 2023-11-02T01:23:14.515Z 4 | -- ------------------------------ 5 | 6 | 7 | -- ------------------------------ 8 | -- OPTION 9 | -- ------------------------------ 10 | 11 | OPTION IMPORT; 12 | 13 | -- ------------------------------ 14 | -- PARAMS 15 | -- ------------------------------ 16 | 17 | DEFINE PARAM $latest_user VALUE (SELECT username, slug, created_at FROM user ORDER BY created_at DESC LIMIT 1); 18 | DEFINE PARAM $total_posts VALUE count((SELECT id FROM post)); 19 | DEFINE PARAM $total_users VALUE count((SELECT id FROM user)); 20 | DEFINE PARAM $your_posts VALUE count((SELECT id FROM post WHERE author = $author)); 21 | 22 | -- ------------------------------ 23 | -- SCOPES 24 | -- ------------------------------ 25 | 26 | DEFINE SCOPE user SESSION 1h SIGNUP (CREATE user CONTENT { created_at: time::now(), password: crypto::argon2::generate($password), username: $username }) SIGNIN (SELECT * OMIT password FROM user WHERE (username = $username AND crypto::argon2::compare(password, $password))); 27 | 28 | -- ------------------------------ 29 | -- TABLE: post 30 | -- ------------------------------ 31 | 32 | DEFINE TABLE post SCHEMAFULL; 33 | 34 | DEFINE FIELD author ON post TYPE record; 35 | DEFINE FIELD content ON post TYPE string; 36 | DEFINE FIELD created_at ON post TYPE datetime DEFAULT time::now(); 37 | DEFINE FIELD slug ON post FLEXIBLE TYPE string DEFAULT string::slug(title); 38 | DEFINE FIELD title ON post TYPE string; 39 | DEFINE FIELD updated_at ON post TYPE option VALUE time::now(); 40 | 41 | DEFINE INDEX author ON post FIELDS author; 42 | DEFINE INDEX slug ON post FIELDS slug UNIQUE; 43 | DEFINE INDEX title ON post FIELDS title UNIQUE; 44 | 45 | -- ------------------------------ 46 | -- TABLE: user 47 | -- ------------------------------ 48 | 49 | DEFINE TABLE user SCHEMAFULL; 50 | 51 | DEFINE FIELD created_at ON user TYPE datetime DEFAULT time::now(); 52 | DEFINE FIELD etoro ON user TYPE option ASSERT $value = NONE OR string::is::url($value); 53 | DEFINE FIELD github ON user TYPE option ASSERT $value = NONE OR string::is::url($value); 54 | DEFINE FIELD linkedin ON user TYPE option ASSERT $value = NONE OR string::is::url($value); 55 | DEFINE FIELD password ON user TYPE string PERMISSIONS FOR select, create, update, delete WHERE id = $auth.id; 56 | DEFINE FIELD slug ON user TYPE string DEFAULT string::slug(username); 57 | DEFINE FIELD updated_at ON user TYPE option VALUE time::now(); 58 | DEFINE FIELD username ON user TYPE string; 59 | DEFINE FIELD website ON user TYPE option ASSERT $value = NONE OR string::is::url($value); 60 | 61 | DEFINE INDEX slug ON user FIELDS slug UNIQUE; 62 | DEFINE INDEX username ON user FIELDS username UNIQUE; 63 | 64 | -- ------------------------------ 65 | -- TABLE: username_lookup 66 | -- ------------------------------ 67 | 68 | DEFINE TABLE username_lookup SCHEMALESS AS SELECT username FROM user PERMISSIONS FOR select FULL, FOR create, update, delete NONE; 69 | 70 | DEFINE INDEX username_lookup ON username_lookup FIELDS username; 71 | 72 | -- ------------------------------ 73 | -- TABLE: your_posts 74 | -- ------------------------------ 75 | 76 | DEFINE TABLE your_posts SCHEMALESS AS SELECT *, author[*] FROM post PERMISSIONS FOR select WHERE author = $auth.id, FOR create, update, delete NONE; -------------------------------------------------------------------------------- /db/surreal.db/000009.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskar-gmerek/surreal-sveltekit/be60e3a4e47cc5f0b2ad8c8deceec71c7c9dd440/db/surreal.db/000009.log -------------------------------------------------------------------------------- /db/surreal.db/000013.sst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskar-gmerek/surreal-sveltekit/be60e3a4e47cc5f0b2ad8c8deceec71c7c9dd440/db/surreal.db/000013.sst -------------------------------------------------------------------------------- /db/surreal.db/CURRENT: -------------------------------------------------------------------------------- 1 | MANIFEST-000010 2 | -------------------------------------------------------------------------------- /db/surreal.db/IDENTITY: -------------------------------------------------------------------------------- 1 | b6357799-b23c-4021-8f6c-fc98d78f3b43 -------------------------------------------------------------------------------- /db/surreal.db/LOCK: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskar-gmerek/surreal-sveltekit/be60e3a4e47cc5f0b2ad8c8deceec71c7c9dd440/db/surreal.db/LOCK -------------------------------------------------------------------------------- /db/surreal.db/LOG.old.1698801593622438: -------------------------------------------------------------------------------- 1 | 2023/11/01-01:05:16.130532 1 RocksDB version: 8.1.1 2 | 2023/11/01-01:05:16.131037 1 Compile date 2023-04-06 16:38:52 3 | 2023/11/01-01:05:16.131071 1 DB SUMMARY 4 | 2023/11/01-01:05:16.131073 1 DB Session ID: PNKWXA6KIGKQ5IQMMG7K 5 | 2023/11/01-01:05:16.131734 1 SST files in /db/surreal.db dir, Total Num: 0, files: 6 | 2023/11/01-01:05:16.131739 1 Write Ahead Log file in /db/surreal.db: 7 | 2023/11/01-01:05:16.131741 1 Options.error_if_exists: 0 8 | 2023/11/01-01:05:16.131742 1 Options.create_if_missing: 1 9 | 2023/11/01-01:05:16.131743 1 Options.paranoid_checks: 1 10 | 2023/11/01-01:05:16.131744 1 Options.flush_verify_memtable_count: 1 11 | 2023/11/01-01:05:16.131745 1 Options.track_and_verify_wals_in_manifest: 0 12 | 2023/11/01-01:05:16.131745 1 Options.verify_sst_unique_id_in_manifest: 1 13 | 2023/11/01-01:05:16.131746 1 Options.env: 0xaaaaeb6c7670 14 | 2023/11/01-01:05:16.131748 1 Options.fs: PosixFileSystem 15 | 2023/11/01-01:05:16.131749 1 Options.info_log: 0xaaab13b6ac00 16 | 2023/11/01-01:05:16.131749 1 Options.max_file_opening_threads: 16 17 | 2023/11/01-01:05:16.131750 1 Options.statistics: (nil) 18 | 2023/11/01-01:05:16.131751 1 Options.use_fsync: 0 19 | 2023/11/01-01:05:16.131752 1 Options.max_log_file_size: 0 20 | 2023/11/01-01:05:16.131753 1 Options.max_manifest_file_size: 1073741824 21 | 2023/11/01-01:05:16.131754 1 Options.log_file_time_to_roll: 0 22 | 2023/11/01-01:05:16.131754 1 Options.keep_log_file_num: 1000 23 | 2023/11/01-01:05:16.131755 1 Options.recycle_log_file_num: 0 24 | 2023/11/01-01:05:16.131756 1 Options.allow_fallocate: 1 25 | 2023/11/01-01:05:16.131757 1 Options.allow_mmap_reads: 0 26 | 2023/11/01-01:05:16.131758 1 Options.allow_mmap_writes: 0 27 | 2023/11/01-01:05:16.131759 1 Options.use_direct_reads: 0 28 | 2023/11/01-01:05:16.131760 1 Options.use_direct_io_for_flush_and_compaction: 0 29 | 2023/11/01-01:05:16.131761 1 Options.create_missing_column_families: 0 30 | 2023/11/01-01:05:16.131761 1 Options.db_log_dir: 31 | 2023/11/01-01:05:16.131763 1 Options.wal_dir: 32 | 2023/11/01-01:05:16.131764 1 Options.table_cache_numshardbits: 6 33 | 2023/11/01-01:05:16.131764 1 Options.WAL_ttl_seconds: 0 34 | 2023/11/01-01:05:16.131765 1 Options.WAL_size_limit_MB: 0 35 | 2023/11/01-01:05:16.131766 1 Options.max_write_batch_group_size_bytes: 1048576 36 | 2023/11/01-01:05:16.131767 1 Options.manifest_preallocation_size: 4194304 37 | 2023/11/01-01:05:16.131767 1 Options.is_fd_close_on_exec: 1 38 | 2023/11/01-01:05:16.131768 1 Options.advise_random_on_open: 1 39 | 2023/11/01-01:05:16.131769 1 Options.db_write_buffer_size: 0 40 | 2023/11/01-01:05:16.131770 1 Options.write_buffer_manager: 0xaaab13b35150 41 | 2023/11/01-01:05:16.131771 1 Options.access_hint_on_compaction_start: 1 42 | 2023/11/01-01:05:16.131771 1 Options.random_access_max_buffer_size: 1048576 43 | 2023/11/01-01:05:16.131772 1 Options.use_adaptive_mutex: 0 44 | 2023/11/01-01:05:16.131773 1 Options.rate_limiter: (nil) 45 | 2023/11/01-01:05:16.131779 1 Options.sst_file_manager.rate_bytes_per_sec: 0 46 | 2023/11/01-01:05:16.131780 1 Options.wal_recovery_mode: 2 47 | 2023/11/01-01:05:16.131781 1 Options.enable_thread_tracking: 0 48 | 2023/11/01-01:05:16.131782 1 Options.enable_pipelined_write: 0 49 | 2023/11/01-01:05:16.131782 1 Options.unordered_write: 0 50 | 2023/11/01-01:05:16.131783 1 Options.allow_concurrent_memtable_write: 1 51 | 2023/11/01-01:05:16.131784 1 Options.enable_write_thread_adaptive_yield: 1 52 | 2023/11/01-01:05:16.131785 1 Options.write_thread_max_yield_usec: 100 53 | 2023/11/01-01:05:16.131789 1 Options.write_thread_slow_yield_usec: 3 54 | 2023/11/01-01:05:16.131790 1 Options.row_cache: None 55 | 2023/11/01-01:05:16.131790 1 Options.wal_filter: None 56 | 2023/11/01-01:05:16.131792 1 Options.avoid_flush_during_recovery: 0 57 | 2023/11/01-01:05:16.131792 1 Options.allow_ingest_behind: 0 58 | 2023/11/01-01:05:16.131793 1 Options.two_write_queues: 0 59 | 2023/11/01-01:05:16.131794 1 Options.manual_wal_flush: 0 60 | 2023/11/01-01:05:16.131795 1 Options.wal_compression: 0 61 | 2023/11/01-01:05:16.131795 1 Options.atomic_flush: 0 62 | 2023/11/01-01:05:16.131796 1 Options.avoid_unnecessary_blocking_io: 0 63 | 2023/11/01-01:05:16.131797 1 Options.persist_stats_to_disk: 0 64 | 2023/11/01-01:05:16.131798 1 Options.write_dbid_to_manifest: 0 65 | 2023/11/01-01:05:16.131799 1 Options.log_readahead_size: 0 66 | 2023/11/01-01:05:16.131799 1 Options.file_checksum_gen_factory: Unknown 67 | 2023/11/01-01:05:16.131800 1 Options.best_efforts_recovery: 0 68 | 2023/11/01-01:05:16.131801 1 Options.max_bgerror_resume_count: 2147483647 69 | 2023/11/01-01:05:16.131802 1 Options.bgerror_resume_retry_interval: 1000000 70 | 2023/11/01-01:05:16.131803 1 Options.allow_data_in_errors: 0 71 | 2023/11/01-01:05:16.131803 1 Options.db_host_id: __hostname__ 72 | 2023/11/01-01:05:16.131804 1 Options.enforce_single_del_contracts: true 73 | 2023/11/01-01:05:16.131806 1 Options.max_background_jobs: 2 74 | 2023/11/01-01:05:16.131806 1 Options.max_background_compactions: -1 75 | 2023/11/01-01:05:16.131807 1 Options.max_subcompactions: 1 76 | 2023/11/01-01:05:16.131808 1 Options.avoid_flush_during_shutdown: 0 77 | 2023/11/01-01:05:16.131809 1 Options.writable_file_max_buffer_size: 1048576 78 | 2023/11/01-01:05:16.131809 1 Options.delayed_write_rate : 16777216 79 | 2023/11/01-01:05:16.131810 1 Options.max_total_wal_size: 0 80 | 2023/11/01-01:05:16.131811 1 Options.delete_obsolete_files_period_micros: 21600000000 81 | 2023/11/01-01:05:16.131812 1 Options.stats_dump_period_sec: 600 82 | 2023/11/01-01:05:16.131813 1 Options.stats_persist_period_sec: 600 83 | 2023/11/01-01:05:16.131813 1 Options.stats_history_buffer_size: 1048576 84 | 2023/11/01-01:05:16.131814 1 Options.max_open_files: -1 85 | 2023/11/01-01:05:16.131815 1 Options.bytes_per_sync: 0 86 | 2023/11/01-01:05:16.131816 1 Options.wal_bytes_per_sync: 0 87 | 2023/11/01-01:05:16.131817 1 Options.strict_bytes_per_sync: 0 88 | 2023/11/01-01:05:16.131817 1 Options.compaction_readahead_size: 0 89 | 2023/11/01-01:05:16.131818 1 Options.max_background_flushes: -1 90 | 2023/11/01-01:05:16.131819 1 Compression algorithms supported: 91 | 2023/11/01-01:05:16.131830 1 kZSTD supported: 1 92 | 2023/11/01-01:05:16.131831 1 kXpressCompression supported: 0 93 | 2023/11/01-01:05:16.131832 1 kBZip2Compression supported: 1 94 | 2023/11/01-01:05:16.131832 1 kZSTDNotFinalCompression supported: 1 95 | 2023/11/01-01:05:16.131833 1 kLZ4Compression supported: 1 96 | 2023/11/01-01:05:16.131834 1 kZlibCompression supported: 1 97 | 2023/11/01-01:05:16.131835 1 kLZ4HCCompression supported: 1 98 | 2023/11/01-01:05:16.131836 1 kSnappyCompression supported: 1 99 | 2023/11/01-01:05:16.131838 1 Fast CRC32 supported: Not supported on x86 100 | 2023/11/01-01:05:16.131839 1 DMutex implementation: pthread_mutex_t 101 | 2023/11/01-01:05:16.134935 1 [db/db_impl/db_impl_open.cc:315] Creating manifest 1 102 | 2023/11/01-01:05:16.137337 1 [db/version_set.cc:5661] Recovering from manifest file: /db/surreal.db/MANIFEST-000001 103 | 2023/11/01-01:05:16.138796 1 [db/column_family.cc:619] --------------- Options for column family [default]: 104 | 2023/11/01-01:05:16.138803 1 Options.comparator: leveldb.BytewiseComparator 105 | 2023/11/01-01:05:16.138804 1 Options.merge_operator: None 106 | 2023/11/01-01:05:16.138807 1 Options.compaction_filter: None 107 | 2023/11/01-01:05:16.138808 1 Options.compaction_filter_factory: None 108 | 2023/11/01-01:05:16.138809 1 Options.sst_partitioner_factory: None 109 | 2023/11/01-01:05:16.138810 1 Options.memtable_factory: SkipListFactory 110 | 2023/11/01-01:05:16.138811 1 Options.table_factory: BlockBasedTable 111 | 2023/11/01-01:05:16.138833 1 table_factory options: flush_block_policy_factory: FlushBlockBySizePolicyFactory (0xaaab13b74590) 112 | cache_index_and_filter_blocks: 0 113 | cache_index_and_filter_blocks_with_high_priority: 1 114 | pin_l0_filter_and_index_blocks_in_cache: 0 115 | pin_top_level_index_and_filter: 1 116 | index_type: 0 117 | data_block_index_type: 0 118 | index_shortening: 1 119 | data_block_hash_table_util_ratio: 0.750000 120 | checksum: 4 121 | no_block_cache: 0 122 | block_cache: 0xaaab13b80bd0 123 | block_cache_name: LRUCache 124 | block_cache_options: 125 | capacity : 8388608 126 | num_shard_bits : 4 127 | strict_capacity_limit : 0 128 | memory_allocator : None 129 | high_pri_pool_ratio: 0.000 130 | low_pri_pool_ratio: 0.000 131 | persistent_cache: (nil) 132 | block_size: 4096 133 | block_size_deviation: 10 134 | block_restart_interval: 16 135 | index_block_restart_interval: 1 136 | metadata_block_size: 4096 137 | partition_filters: 0 138 | use_delta_encoding: 1 139 | filter_policy: nullptr 140 | whole_key_filtering: 1 141 | verify_compression: 0 142 | read_amp_bytes_per_bit: 0 143 | format_version: 5 144 | enable_index_compression: 1 145 | block_align: 0 146 | max_auto_readahead_size: 262144 147 | prepopulate_block_cache: 0 148 | initial_auto_readahead_size: 8192 149 | num_file_reads_for_auto_readahead: 2 150 | 2023/11/01-01:05:16.138834 1 Options.write_buffer_size: 67108864 151 | 2023/11/01-01:05:16.138835 1 Options.max_write_buffer_number: 2 152 | 2023/11/01-01:05:16.138836 1 Options.compression: Snappy 153 | 2023/11/01-01:05:16.138837 1 Options.bottommost_compression: Disabled 154 | 2023/11/01-01:05:16.138837 1 Options.prefix_extractor: nullptr 155 | 2023/11/01-01:05:16.138838 1 Options.memtable_insert_with_hint_prefix_extractor: nullptr 156 | 2023/11/01-01:05:16.138839 1 Options.num_levels: 7 157 | 2023/11/01-01:05:16.138840 1 Options.min_write_buffer_number_to_merge: 1 158 | 2023/11/01-01:05:16.138841 1 Options.max_write_buffer_number_to_maintain: 0 159 | 2023/11/01-01:05:16.138841 1 Options.max_write_buffer_size_to_maintain: 134217728 160 | 2023/11/01-01:05:16.138842 1 Options.bottommost_compression_opts.window_bits: -14 161 | 2023/11/01-01:05:16.138843 1 Options.bottommost_compression_opts.level: 32767 162 | 2023/11/01-01:05:16.138844 1 Options.bottommost_compression_opts.strategy: 0 163 | 2023/11/01-01:05:16.138845 1 Options.bottommost_compression_opts.max_dict_bytes: 0 164 | 2023/11/01-01:05:16.138846 1 Options.bottommost_compression_opts.zstd_max_train_bytes: 0 165 | 2023/11/01-01:05:16.138846 1 Options.bottommost_compression_opts.parallel_threads: 1 166 | 2023/11/01-01:05:16.138847 1 Options.bottommost_compression_opts.enabled: false 167 | 2023/11/01-01:05:16.138848 1 Options.bottommost_compression_opts.max_dict_buffer_bytes: 0 168 | 2023/11/01-01:05:16.138849 1 Options.bottommost_compression_opts.use_zstd_dict_trainer: true 169 | 2023/11/01-01:05:16.138850 1 Options.compression_opts.window_bits: -14 170 | 2023/11/01-01:05:16.138851 1 Options.compression_opts.level: 32767 171 | 2023/11/01-01:05:16.138851 1 Options.compression_opts.strategy: 0 172 | 2023/11/01-01:05:16.138852 1 Options.compression_opts.max_dict_bytes: 0 173 | 2023/11/01-01:05:16.138853 1 Options.compression_opts.zstd_max_train_bytes: 0 174 | 2023/11/01-01:05:16.138854 1 Options.compression_opts.use_zstd_dict_trainer: true 175 | 2023/11/01-01:05:16.138854 1 Options.compression_opts.parallel_threads: 1 176 | 2023/11/01-01:05:16.138855 1 Options.compression_opts.enabled: false 177 | 2023/11/01-01:05:16.138856 1 Options.compression_opts.max_dict_buffer_bytes: 0 178 | 2023/11/01-01:05:16.138857 1 Options.level0_file_num_compaction_trigger: 4 179 | 2023/11/01-01:05:16.138858 1 Options.level0_slowdown_writes_trigger: 20 180 | 2023/11/01-01:05:16.138860 1 Options.level0_stop_writes_trigger: 36 181 | 2023/11/01-01:05:16.138861 1 Options.target_file_size_base: 67108864 182 | 2023/11/01-01:05:16.138862 1 Options.target_file_size_multiplier: 1 183 | 2023/11/01-01:05:16.138862 1 Options.max_bytes_for_level_base: 268435456 184 | 2023/11/01-01:05:16.138863 1 Options.level_compaction_dynamic_level_bytes: 0 185 | 2023/11/01-01:05:16.138864 1 Options.max_bytes_for_level_multiplier: 10.000000 186 | 2023/11/01-01:05:16.138865 1 Options.max_bytes_for_level_multiplier_addtl[0]: 1 187 | 2023/11/01-01:05:16.138866 1 Options.max_bytes_for_level_multiplier_addtl[1]: 1 188 | 2023/11/01-01:05:16.138867 1 Options.max_bytes_for_level_multiplier_addtl[2]: 1 189 | 2023/11/01-01:05:16.138868 1 Options.max_bytes_for_level_multiplier_addtl[3]: 1 190 | 2023/11/01-01:05:16.138869 1 Options.max_bytes_for_level_multiplier_addtl[4]: 1 191 | 2023/11/01-01:05:16.138870 1 Options.max_bytes_for_level_multiplier_addtl[5]: 1 192 | 2023/11/01-01:05:16.138870 1 Options.max_bytes_for_level_multiplier_addtl[6]: 1 193 | 2023/11/01-01:05:16.138871 1 Options.max_sequential_skip_in_iterations: 8 194 | 2023/11/01-01:05:16.138872 1 Options.max_compaction_bytes: 1677721600 195 | 2023/11/01-01:05:16.138873 1 Options.ignore_max_compaction_bytes_for_input: true 196 | 2023/11/01-01:05:16.138874 1 Options.arena_block_size: 1048576 197 | 2023/11/01-01:05:16.138874 1 Options.soft_pending_compaction_bytes_limit: 68719476736 198 | 2023/11/01-01:05:16.138875 1 Options.hard_pending_compaction_bytes_limit: 274877906944 199 | 2023/11/01-01:05:16.138876 1 Options.disable_auto_compactions: 0 200 | 2023/11/01-01:05:16.138878 1 Options.compaction_style: kCompactionStyleLevel 201 | 2023/11/01-01:05:16.138879 1 Options.compaction_pri: kMinOverlappingRatio 202 | 2023/11/01-01:05:16.138880 1 Options.compaction_options_universal.size_ratio: 1 203 | 2023/11/01-01:05:16.138881 1 Options.compaction_options_universal.min_merge_width: 2 204 | 2023/11/01-01:05:16.138881 1 Options.compaction_options_universal.max_merge_width: 4294967295 205 | 2023/11/01-01:05:16.138882 1 Options.compaction_options_universal.max_size_amplification_percent: 200 206 | 2023/11/01-01:05:16.138883 1 Options.compaction_options_universal.compression_size_percent: -1 207 | 2023/11/01-01:05:16.138884 1 Options.compaction_options_universal.stop_style: kCompactionStopStyleTotalSize 208 | 2023/11/01-01:05:16.138885 1 Options.compaction_options_fifo.max_table_files_size: 1073741824 209 | 2023/11/01-01:05:16.138886 1 Options.compaction_options_fifo.allow_compaction: 0 210 | 2023/11/01-01:05:16.138889 1 Options.table_properties_collectors: 211 | 2023/11/01-01:05:16.138890 1 Options.inplace_update_support: 0 212 | 2023/11/01-01:05:16.138891 1 Options.inplace_update_num_locks: 10000 213 | 2023/11/01-01:05:16.138892 1 Options.memtable_prefix_bloom_size_ratio: 0.000000 214 | 2023/11/01-01:05:16.138892 1 Options.memtable_whole_key_filtering: 0 215 | 2023/11/01-01:05:16.138939 1 Options.memtable_huge_page_size: 0 216 | 2023/11/01-01:05:16.138950 1 Options.bloom_locality: 0 217 | 2023/11/01-01:05:16.138952 1 Options.max_successive_merges: 0 218 | 2023/11/01-01:05:16.138953 1 Options.optimize_filters_for_hits: 0 219 | 2023/11/01-01:05:16.138955 1 Options.paranoid_file_checks: 0 220 | 2023/11/01-01:05:16.138956 1 Options.force_consistency_checks: 1 221 | 2023/11/01-01:05:16.138957 1 Options.report_bg_io_stats: 0 222 | 2023/11/01-01:05:16.138959 1 Options.ttl: 2592000 223 | 2023/11/01-01:05:16.138960 1 Options.periodic_compaction_seconds: 0 224 | 2023/11/01-01:05:16.138961 1 Options.preclude_last_level_data_seconds: 0 225 | 2023/11/01-01:05:16.138963 1 Options.preserve_internal_time_seconds: 0 226 | 2023/11/01-01:05:16.138964 1 Options.enable_blob_files: false 227 | 2023/11/01-01:05:16.138966 1 Options.min_blob_size: 0 228 | 2023/11/01-01:05:16.138967 1 Options.blob_file_size: 268435456 229 | 2023/11/01-01:05:16.138977 1 Options.blob_compression_type: NoCompression 230 | 2023/11/01-01:05:16.138979 1 Options.enable_blob_garbage_collection: false 231 | 2023/11/01-01:05:16.138980 1 Options.blob_garbage_collection_age_cutoff: 0.250000 232 | 2023/11/01-01:05:16.138984 1 Options.blob_garbage_collection_force_threshold: 1.000000 233 | 2023/11/01-01:05:16.138985 1 Options.blob_compaction_readahead_size: 0 234 | 2023/11/01-01:05:16.138987 1 Options.blob_file_starting_level: 0 235 | 2023/11/01-01:05:16.138988 1 Options.experimental_mempurge_threshold: 0.000000 236 | 2023/11/01-01:05:16.141973 1 [db/version_set.cc:5704] Recovered from manifest file:/db/surreal.db/MANIFEST-000001 succeeded,manifest_file_number is 1, next_file_number is 3, last_sequence is 0, log_number is 0,prev_log_number is 0,max_column_family is 0,min_log_number_to_keep is 0 237 | 2023/11/01-01:05:16.141980 1 [db/version_set.cc:5719] Column family [default] (ID 0), log number is 0 238 | 2023/11/01-01:05:16.142794 1 [db/db_impl/db_impl_open.cc:537] DB ID: b6357799-b23c-4021-8f6c-fc98d78f3b43 239 | 2023/11/01-01:05:16.144570 1 [db/version_set.cc:5179] Creating manifest 5 240 | 2023/11/01-01:05:16.154779 1 [db/db_impl/db_impl_open.cc:1976] SstFileManager instance 0xaaab13b80c70 241 | 2023/11/01-01:05:16.156256 1 DB pointer 0xaaab13b58b80 242 | 2023/11/01-01:05:16.157259 34 [db/db_impl/db_impl.cc:1084] ------- DUMPING STATS ------- 243 | 2023/11/01-01:05:16.157270 34 [db/db_impl/db_impl.cc:1086] 244 | ** DB Stats ** 245 | Uptime(secs): 0.0 total, 0.0 interval 246 | Cumulative writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 GB, 0.00 MB/s 247 | Cumulative WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s 248 | Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent 249 | Interval writes: 0 writes, 0 keys, 0 commit groups, 0.0 writes per commit group, ingest: 0.00 MB, 0.00 MB/s 250 | Interval WAL: 0 writes, 0 syncs, 0.00 writes per sync, written: 0.00 GB, 0.00 MB/s 251 | Interval stall: 00:00:0.000 H:M:S, 0.0 percent 252 | Write Stall (count): write-buffer-manager-limit-stops: 0, 253 | ** Compaction Stats [default] ** 254 | Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) 255 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 256 | Sum 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 257 | Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 258 | 259 | ** Compaction Stats [default] ** 260 | Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) 261 | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 262 | 263 | Blob file count: 0, total size: 0.0 GB, garbage size: 0.0 GB, space amp: 0.0 264 | 265 | Uptime(secs): 0.0 total, 0.0 interval 266 | Flush(GB): cumulative 0.000, interval 0.000 267 | AddFile(GB): cumulative 0.000, interval 0.000 268 | AddFile(Total Files): cumulative 0, interval 0 269 | AddFile(L0 Files): cumulative 0, interval 0 270 | AddFile(Keys): cumulative 0, interval 0 271 | Cumulative compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds 272 | Interval compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds 273 | Write Stall (count): cf-l0-file-count-limit-delays-with-ongoing-compaction: 0, cf-l0-file-count-limit-stops-with-ongoing-compaction: 0, l0-file-count-limit-delays: 0, l0-file-count-limit-stops: 0, memtable-limit-delays: 0, memtable-limit-stops: 0, pending-compaction-bytes-delays: 0, pending-compaction-bytes-stops: 0, total-delays: 0, total-stops: 0, Block cache LRUCache@0xaaab13b80bd0#1 capacity: 8.00 MB usage: 0.08 KB table_size: 256 occupancy: 87 collections: 1 last_copies: 0 last_secs: 7.2e-05 secs_since: 0 274 | Block cache entry stats(count,size,portion): Misc(1,0.00 KB,0%) 275 | 276 | ** File Read Latency Histogram By Level [default] ** 277 | 2023/11/01-01:15:16.163039 34 [db/db_impl/db_impl.cc:1084] ------- DUMPING STATS ------- 278 | 2023/11/01-01:15:16.164807 34 [db/db_impl/db_impl.cc:1086] 279 | ** DB Stats ** 280 | Uptime(secs): 600.0 total, 600.0 interval 281 | Cumulative writes: 166 writes, 197 keys, 166 commit groups, 1.0 writes per commit group, ingest: 0.00 GB, 0.00 MB/s 282 | Cumulative WAL: 166 writes, 0 syncs, 166.00 writes per sync, written: 0.00 GB, 0.00 MB/s 283 | Cumulative stall: 00:00:0.000 H:M:S, 0.0 percent 284 | Interval writes: 166 writes, 197 keys, 166 commit groups, 1.0 writes per commit group, ingest: 0.02 MB, 0.00 MB/s 285 | Interval WAL: 166 writes, 0 syncs, 166.00 writes per sync, written: 0.00 GB, 0.00 MB/s 286 | Interval stall: 00:00:0.000 H:M:S, 0.0 percent 287 | Write Stall (count): write-buffer-manager-limit-stops: 0, 288 | ** Compaction Stats [default] ** 289 | Level Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) 290 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 291 | Sum 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 292 | Int 0/0 0.00 KB 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0 0.000 0 0 0.0 0.0 293 | 294 | ** Compaction Stats [default] ** 295 | Priority Files Size Score Read(GB) Rn(GB) Rnp1(GB) Write(GB) Wnew(GB) Moved(GB) W-Amp Rd(MB/s) Wr(MB/s) Comp(sec) CompMergeCPU(sec) Comp(cnt) Avg(sec) KeyIn KeyDrop Rblob(GB) Wblob(GB) 296 | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 297 | 298 | Blob file count: 0, total size: 0.0 GB, garbage size: 0.0 GB, space amp: 0.0 299 | 300 | Uptime(secs): 600.0 total, 600.0 interval 301 | Flush(GB): cumulative 0.000, interval 0.000 302 | AddFile(GB): cumulative 0.000, interval 0.000 303 | AddFile(Total Files): cumulative 0, interval 0 304 | AddFile(L0 Files): cumulative 0, interval 0 305 | AddFile(Keys): cumulative 0, interval 0 306 | Cumulative compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds 307 | Interval compaction: 0.00 GB write, 0.00 MB/s write, 0.00 GB read, 0.00 MB/s read, 0.0 seconds 308 | Write Stall (count): cf-l0-file-count-limit-delays-with-ongoing-compaction: 0, cf-l0-file-count-limit-stops-with-ongoing-compaction: 0, l0-file-count-limit-delays: 0, l0-file-count-limit-stops: 0, memtable-limit-delays: 0, memtable-limit-stops: 0, pending-compaction-bytes-delays: 0, pending-compaction-bytes-stops: 0, total-delays: 0, total-stops: 0, Block cache LRUCache@0xaaab13b80bd0#1 capacity: 8.00 MB usage: 0.08 KB table_size: 256 occupancy: 87 collections: 2 last_copies: 0 last_secs: 0.000468 secs_since: 0 309 | Block cache entry stats(count,size,portion): Misc(1,0.00 KB,0%) 310 | 311 | ** File Read Latency Histogram By Level [default] ** 312 | -------------------------------------------------------------------------------- /db/surreal.db/MANIFEST-000010: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oskar-gmerek/surreal-sveltekit/be60e3a4e47cc5f0b2ad8c8deceec71c7c9dd440/db/surreal.db/MANIFEST-000010 -------------------------------------------------------------------------------- /db/surreal.db/OPTIONS-000007: -------------------------------------------------------------------------------- 1 | # This is a RocksDB option file. 2 | # 3 | # For detailed file format spec, please refer to the example file 4 | # in examples/rocksdb_option_file_example.ini 5 | # 6 | 7 | [Version] 8 | rocksdb_version=8.1.1 9 | options_file_version=1.1 10 | 11 | [DBOptions] 12 | compaction_readahead_size=0 13 | strict_bytes_per_sync=false 14 | bytes_per_sync=0 15 | max_background_jobs=2 16 | avoid_flush_during_shutdown=false 17 | max_background_flushes=-1 18 | delayed_write_rate=16777216 19 | max_open_files=-1 20 | max_subcompactions=1 21 | writable_file_max_buffer_size=1048576 22 | wal_bytes_per_sync=0 23 | max_background_compactions=-1 24 | max_total_wal_size=0 25 | delete_obsolete_files_period_micros=21600000000 26 | stats_dump_period_sec=600 27 | stats_history_buffer_size=1048576 28 | stats_persist_period_sec=600 29 | enforce_single_del_contracts=true 30 | lowest_used_cache_tier=kNonVolatileBlockTier 31 | bgerror_resume_retry_interval=1000000 32 | best_efforts_recovery=false 33 | log_readahead_size=0 34 | write_dbid_to_manifest=false 35 | wal_compression=kNoCompression 36 | manual_wal_flush=false 37 | db_host_id=__hostname__ 38 | two_write_queues=false 39 | random_access_max_buffer_size=1048576 40 | avoid_unnecessary_blocking_io=false 41 | skip_checking_sst_file_sizes_on_db_open=false 42 | flush_verify_memtable_count=true 43 | fail_if_options_file_error=false 44 | atomic_flush=false 45 | verify_sst_unique_id_in_manifest=true 46 | skip_stats_update_on_db_open=false 47 | track_and_verify_wals_in_manifest=false 48 | paranoid_checks=true 49 | create_if_missing=true 50 | max_write_batch_group_size_bytes=1048576 51 | avoid_flush_during_recovery=false 52 | file_checksum_gen_factory=nullptr 53 | enable_thread_tracking=false 54 | allow_fallocate=true 55 | allow_data_in_errors=false 56 | error_if_exists=false 57 | use_direct_io_for_flush_and_compaction=false 58 | create_missing_column_families=false 59 | WAL_size_limit_MB=0 60 | use_direct_reads=false 61 | persist_stats_to_disk=false 62 | allow_mmap_reads=false 63 | allow_mmap_writes=false 64 | use_adaptive_mutex=false 65 | allow_2pc=false 66 | is_fd_close_on_exec=true 67 | max_log_file_size=0 68 | access_hint_on_compaction_start=NORMAL 69 | max_file_opening_threads=16 70 | wal_filter=nullptr 71 | use_fsync=false 72 | table_cache_numshardbits=6 73 | dump_malloc_stats=false 74 | db_write_buffer_size=0 75 | allow_ingest_behind=false 76 | keep_log_file_num=1000 77 | max_bgerror_resume_count=2147483647 78 | allow_concurrent_memtable_write=true 79 | recycle_log_file_num=0 80 | log_file_time_to_roll=0 81 | manifest_preallocation_size=4194304 82 | enable_write_thread_adaptive_yield=true 83 | WAL_ttl_seconds=0 84 | max_manifest_file_size=1073741824 85 | wal_recovery_mode=kPointInTimeRecovery 86 | enable_pipelined_write=false 87 | write_thread_slow_yield_usec=3 88 | unordered_write=false 89 | write_thread_max_yield_usec=100 90 | advise_random_on_open=true 91 | info_log_level=INFO_LEVEL 92 | 93 | 94 | [CFOptions "default"] 95 | compression_opts={max_dict_buffer_bytes=0;enabled=false;max_dict_bytes=0;parallel_threads=1;zstd_max_train_bytes=0;level=32767;use_zstd_dict_trainer=true;strategy=0;window_bits=-14;} 96 | memtable_protection_bytes_per_key=0 97 | target_file_size_multiplier=1 98 | report_bg_io_stats=false 99 | write_buffer_size=67108864 100 | memtable_huge_page_size=0 101 | max_successive_merges=0 102 | max_write_buffer_number=2 103 | prefix_extractor=nullptr 104 | bottommost_compression_opts={max_dict_buffer_bytes=0;enabled=false;max_dict_bytes=0;parallel_threads=1;zstd_max_train_bytes=0;level=32767;use_zstd_dict_trainer=true;strategy=0;window_bits=-14;} 105 | paranoid_file_checks=false 106 | blob_garbage_collection_force_threshold=1.000000 107 | enable_blob_files=false 108 | blob_file_starting_level=0 109 | memtable_prefix_bloom_size_ratio=0.000000 110 | inplace_update_num_locks=10000 111 | blob_compaction_readahead_size=0 112 | ignore_max_compaction_bytes_for_input=true 113 | arena_block_size=1048576 114 | level0_stop_writes_trigger=36 115 | blob_compression_type=kNoCompression 116 | level0_slowdown_writes_trigger=20 117 | hard_pending_compaction_bytes_limit=274877906944 118 | soft_pending_compaction_bytes_limit=68719476736 119 | target_file_size_base=67108864 120 | level0_file_num_compaction_trigger=4 121 | max_compaction_bytes=1677721600 122 | disable_auto_compactions=false 123 | check_flush_compaction_key_order=true 124 | min_blob_size=0 125 | memtable_whole_key_filtering=false 126 | max_bytes_for_level_base=268435456 127 | last_level_temperature=kUnknown 128 | compaction_options_fifo={allow_compaction=false;age_for_warm=0;max_table_files_size=1073741824;} 129 | max_bytes_for_level_multiplier=10.000000 130 | max_bytes_for_level_multiplier_additional=1:1:1:1:1:1:1 131 | max_sequential_skip_in_iterations=8 132 | prepopulate_blob_cache=kDisable 133 | compression=kSnappyCompression 134 | compaction_options_universal={incremental=false;compression_size_percent=-1;allow_trivial_move=false;max_size_amplification_percent=200;max_merge_width=4294967295;stop_style=kCompactionStopStyleTotalSize;min_merge_width=2;size_ratio=1;} 135 | blob_garbage_collection_age_cutoff=0.250000 136 | ttl=2592000 137 | periodic_compaction_seconds=0 138 | sample_for_compression=0 139 | blob_file_size=268435456 140 | enable_blob_garbage_collection=false 141 | experimental_mempurge_threshold=0.000000 142 | bottommost_compression=kDisableCompressionOption 143 | min_write_buffer_number_to_merge=1 144 | preserve_internal_time_seconds=0 145 | preclude_last_level_data_seconds=0 146 | sst_partitioner_factory=nullptr 147 | num_levels=7 148 | force_consistency_checks=true 149 | memtable_insert_with_hint_prefix_extractor=nullptr 150 | memtable_factory=SkipListFactory 151 | level_compaction_dynamic_file_size=true 152 | max_write_buffer_number_to_maintain=0 153 | optimize_filters_for_hits=false 154 | level_compaction_dynamic_level_bytes=false 155 | compaction_style=kCompactionStyleLevel 156 | compaction_filter=nullptr 157 | inplace_update_support=false 158 | merge_operator=nullptr 159 | table_factory=BlockBasedTable 160 | bloom_locality=0 161 | comparator=leveldb.BytewiseComparator 162 | compaction_filter_factory=nullptr 163 | max_write_buffer_size_to_maintain=134217728 164 | compaction_pri=kMinOverlappingRatio 165 | 166 | [TableOptions/BlockBasedTable "default"] 167 | initial_auto_readahead_size=8192 168 | pin_top_level_index_and_filter=true 169 | block_align=false 170 | block_size_deviation=10 171 | checksum=kXXH3 172 | index_shortening=kShortenSeparators 173 | num_file_reads_for_auto_readahead=2 174 | whole_key_filtering=true 175 | data_block_index_type=kDataBlockBinarySearch 176 | index_type=kBinarySearch 177 | no_block_cache=false 178 | index_block_restart_interval=1 179 | data_block_hash_table_util_ratio=0.750000 180 | prepopulate_block_cache=kDisable 181 | pin_l0_filter_and_index_blocks_in_cache=false 182 | filter_policy=nullptr 183 | cache_index_and_filter_blocks_with_high_priority=true 184 | verify_compression=false 185 | block_restart_interval=16 186 | max_auto_readahead_size=262144 187 | flush_block_policy_factory=FlushBlockBySizePolicyFactory 188 | partition_filters=false 189 | cache_index_and_filter_blocks=false 190 | block_size=4096 191 | metadata_block_size=4096 192 | optimize_filters_for_memory=false 193 | detect_filter_construct_corruption=false 194 | format_version=5 195 | metadata_cache_options={unpartitioned_pinning=kFallback;partition_pinning=kFallback;top_level_index_pinning=kFallback;} 196 | read_amp_bytes_per_bit=0 197 | enable_index_compression=true 198 | 199 | -------------------------------------------------------------------------------- /db/surreal.db/OPTIONS-000012: -------------------------------------------------------------------------------- 1 | # This is a RocksDB option file. 2 | # 3 | # For detailed file format spec, please refer to the example file 4 | # in examples/rocksdb_option_file_example.ini 5 | # 6 | 7 | [Version] 8 | rocksdb_version=8.1.1 9 | options_file_version=1.1 10 | 11 | [DBOptions] 12 | compaction_readahead_size=0 13 | strict_bytes_per_sync=false 14 | bytes_per_sync=0 15 | max_background_jobs=2 16 | avoid_flush_during_shutdown=false 17 | max_background_flushes=-1 18 | delayed_write_rate=16777216 19 | max_open_files=-1 20 | max_subcompactions=1 21 | writable_file_max_buffer_size=1048576 22 | wal_bytes_per_sync=0 23 | max_background_compactions=-1 24 | max_total_wal_size=0 25 | delete_obsolete_files_period_micros=21600000000 26 | stats_dump_period_sec=600 27 | stats_history_buffer_size=1048576 28 | stats_persist_period_sec=600 29 | enforce_single_del_contracts=true 30 | lowest_used_cache_tier=kNonVolatileBlockTier 31 | bgerror_resume_retry_interval=1000000 32 | best_efforts_recovery=false 33 | log_readahead_size=0 34 | write_dbid_to_manifest=false 35 | wal_compression=kNoCompression 36 | manual_wal_flush=false 37 | db_host_id=__hostname__ 38 | two_write_queues=false 39 | random_access_max_buffer_size=1048576 40 | avoid_unnecessary_blocking_io=false 41 | skip_checking_sst_file_sizes_on_db_open=false 42 | flush_verify_memtable_count=true 43 | fail_if_options_file_error=false 44 | atomic_flush=false 45 | verify_sst_unique_id_in_manifest=true 46 | skip_stats_update_on_db_open=false 47 | track_and_verify_wals_in_manifest=false 48 | paranoid_checks=true 49 | create_if_missing=true 50 | max_write_batch_group_size_bytes=1048576 51 | avoid_flush_during_recovery=false 52 | file_checksum_gen_factory=nullptr 53 | enable_thread_tracking=false 54 | allow_fallocate=true 55 | allow_data_in_errors=false 56 | error_if_exists=false 57 | use_direct_io_for_flush_and_compaction=false 58 | create_missing_column_families=false 59 | WAL_size_limit_MB=0 60 | use_direct_reads=false 61 | persist_stats_to_disk=false 62 | allow_mmap_reads=false 63 | allow_mmap_writes=false 64 | use_adaptive_mutex=false 65 | allow_2pc=false 66 | is_fd_close_on_exec=true 67 | max_log_file_size=0 68 | access_hint_on_compaction_start=NORMAL 69 | max_file_opening_threads=16 70 | wal_filter=nullptr 71 | use_fsync=false 72 | table_cache_numshardbits=6 73 | dump_malloc_stats=false 74 | db_write_buffer_size=0 75 | allow_ingest_behind=false 76 | keep_log_file_num=1000 77 | max_bgerror_resume_count=2147483647 78 | allow_concurrent_memtable_write=true 79 | recycle_log_file_num=0 80 | log_file_time_to_roll=0 81 | manifest_preallocation_size=4194304 82 | enable_write_thread_adaptive_yield=true 83 | WAL_ttl_seconds=0 84 | max_manifest_file_size=1073741824 85 | wal_recovery_mode=kPointInTimeRecovery 86 | enable_pipelined_write=false 87 | write_thread_slow_yield_usec=3 88 | unordered_write=false 89 | write_thread_max_yield_usec=100 90 | advise_random_on_open=true 91 | info_log_level=INFO_LEVEL 92 | 93 | 94 | [CFOptions "default"] 95 | compression_opts={max_dict_buffer_bytes=0;enabled=false;max_dict_bytes=0;parallel_threads=1;zstd_max_train_bytes=0;level=32767;use_zstd_dict_trainer=true;strategy=0;window_bits=-14;} 96 | memtable_protection_bytes_per_key=0 97 | target_file_size_multiplier=1 98 | report_bg_io_stats=false 99 | write_buffer_size=67108864 100 | memtable_huge_page_size=0 101 | max_successive_merges=0 102 | max_write_buffer_number=2 103 | prefix_extractor=nullptr 104 | bottommost_compression_opts={max_dict_buffer_bytes=0;enabled=false;max_dict_bytes=0;parallel_threads=1;zstd_max_train_bytes=0;level=32767;use_zstd_dict_trainer=true;strategy=0;window_bits=-14;} 105 | paranoid_file_checks=false 106 | blob_garbage_collection_force_threshold=1.000000 107 | enable_blob_files=false 108 | blob_file_starting_level=0 109 | memtable_prefix_bloom_size_ratio=0.000000 110 | inplace_update_num_locks=10000 111 | blob_compaction_readahead_size=0 112 | ignore_max_compaction_bytes_for_input=true 113 | arena_block_size=1048576 114 | level0_stop_writes_trigger=36 115 | blob_compression_type=kNoCompression 116 | level0_slowdown_writes_trigger=20 117 | hard_pending_compaction_bytes_limit=274877906944 118 | soft_pending_compaction_bytes_limit=68719476736 119 | target_file_size_base=67108864 120 | level0_file_num_compaction_trigger=4 121 | max_compaction_bytes=1677721600 122 | disable_auto_compactions=false 123 | check_flush_compaction_key_order=true 124 | min_blob_size=0 125 | memtable_whole_key_filtering=false 126 | max_bytes_for_level_base=268435456 127 | last_level_temperature=kUnknown 128 | compaction_options_fifo={allow_compaction=false;age_for_warm=0;max_table_files_size=1073741824;} 129 | max_bytes_for_level_multiplier=10.000000 130 | max_bytes_for_level_multiplier_additional=1:1:1:1:1:1:1 131 | max_sequential_skip_in_iterations=8 132 | prepopulate_blob_cache=kDisable 133 | compression=kSnappyCompression 134 | compaction_options_universal={incremental=false;compression_size_percent=-1;allow_trivial_move=false;max_size_amplification_percent=200;max_merge_width=4294967295;stop_style=kCompactionStopStyleTotalSize;min_merge_width=2;size_ratio=1;} 135 | blob_garbage_collection_age_cutoff=0.250000 136 | ttl=2592000 137 | periodic_compaction_seconds=0 138 | sample_for_compression=0 139 | blob_file_size=268435456 140 | enable_blob_garbage_collection=false 141 | experimental_mempurge_threshold=0.000000 142 | bottommost_compression=kDisableCompressionOption 143 | min_write_buffer_number_to_merge=1 144 | preserve_internal_time_seconds=0 145 | preclude_last_level_data_seconds=0 146 | sst_partitioner_factory=nullptr 147 | num_levels=7 148 | force_consistency_checks=true 149 | memtable_insert_with_hint_prefix_extractor=nullptr 150 | memtable_factory=SkipListFactory 151 | level_compaction_dynamic_file_size=true 152 | max_write_buffer_number_to_maintain=0 153 | optimize_filters_for_hits=false 154 | level_compaction_dynamic_level_bytes=false 155 | compaction_style=kCompactionStyleLevel 156 | compaction_filter=nullptr 157 | inplace_update_support=false 158 | merge_operator=nullptr 159 | table_factory=BlockBasedTable 160 | bloom_locality=0 161 | comparator=leveldb.BytewiseComparator 162 | compaction_filter_factory=nullptr 163 | max_write_buffer_size_to_maintain=134217728 164 | compaction_pri=kMinOverlappingRatio 165 | 166 | [TableOptions/BlockBasedTable "default"] 167 | initial_auto_readahead_size=8192 168 | pin_top_level_index_and_filter=true 169 | block_align=false 170 | block_size_deviation=10 171 | checksum=kXXH3 172 | index_shortening=kShortenSeparators 173 | num_file_reads_for_auto_readahead=2 174 | whole_key_filtering=true 175 | data_block_index_type=kDataBlockBinarySearch 176 | index_type=kBinarySearch 177 | no_block_cache=false 178 | index_block_restart_interval=1 179 | data_block_hash_table_util_ratio=0.750000 180 | prepopulate_block_cache=kDisable 181 | pin_l0_filter_and_index_blocks_in_cache=false 182 | filter_policy=nullptr 183 | cache_index_and_filter_blocks_with_high_priority=true 184 | verify_compression=false 185 | block_restart_interval=16 186 | max_auto_readahead_size=262144 187 | flush_block_policy_factory=FlushBlockBySizePolicyFactory 188 | partition_filters=false 189 | cache_index_and_filter_blocks=false 190 | block_size=4096 191 | metadata_block_size=4096 192 | optimize_filters_for_memory=false 193 | detect_filter_construct_corruption=false 194 | format_version=5 195 | metadata_cache_options={unpartitioned_pinning=kFallback;partition_pinning=kFallback;top_level_index_pinning=kFallback;} 196 | read_amp_bytes_per_bit=0 197 | enable_index_compression=true 198 | 199 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | database: 3 | container_name: database 4 | restart: unless-stopped 5 | env_file: 6 | - .env 7 | entrypoint: 8 | - /surreal 9 | - start 10 | - --allow-guests 11 | - --auth 12 | - --log 13 | - $DB_LOG_LEVEL 14 | - --user 15 | - $DB_USER 16 | - --pass 17 | - $DB_PASSWORD 18 | - file:/db/surreal.db 19 | volumes: 20 | - ./db:/db 21 | image: surrealdb/surrealdb:v1.1.1 22 | ports: 23 | - 8000:8000 24 | 25 | volumes: 26 | db: 27 | name: db -------------------------------------------------------------------------------- /master.css.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from '@master/css' 2 | import { variables } from '@master/css'; 3 | 4 | export default { 5 | styles: { 6 | body: 'bg:gray-90 fg:gray-20 f:sans f:18' 7 | }, 8 | rules: {}, 9 | variables: { 10 | fontFamily: { 11 | sans: ['akshar', ...variables['font-family'].sans], 12 | }, 13 | fontWeight: { 14 | normal: 400, 15 | bold: 600 16 | } 17 | }, 18 | semantics: {}, 19 | mediaQueries: {}, 20 | animations: {}, 21 | selectors: {}, 22 | functions: {} 23 | } satisfies Config as Config; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "surreal-sveltekit", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "@master/css-server": "2.0.0-rc.13", 6 | "@master/css.svelte": "2.0.0-rc.13", 7 | "@master/normal.css": "2.0.0-rc.13", 8 | "@sveltejs/adapter-auto": "3.1.1", 9 | "@sveltejs/adapter-node": "4.0.1", 10 | "@sveltejs/kit": "2.5.0", 11 | "@sveltejs/vite-plugin-svelte": "3.0.2", 12 | "@typescript-eslint/eslint-plugin": "7.0.1", 13 | "@typescript-eslint/parser": "7.0.1", 14 | "dayjs": "1.11.10", 15 | "eslint": "8.56.0", 16 | "eslint-config-prettier": "9.1.0", 17 | "eslint-plugin-svelte": "2.35.1", 18 | "prettier": "3.2.5", 19 | "prettier-plugin-svelte": "3.1.2", 20 | "surrealdb.js": "0.11.0", 21 | "svelte": "4.2.10", 22 | "svelte-check": "3.6.4", 23 | "sveltekit-superforms": "1.13.4", 24 | "tslib": "2.6.2", 25 | "typescript": "5.3.3", 26 | "vite": "5.1.1", 27 | "vitest": "1.2.2", 28 | "zod": "3.22.4" 29 | }, 30 | "private": true, 31 | "scripts": { 32 | "multitaskum:developum": "docker compose up -d && vite dev --host", 33 | "dev": "vite dev", 34 | "build": "vite build", 35 | "preview": "vite preview", 36 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 37 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 38 | "test": "vitest", 39 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 40 | "format": "prettier --plugin-search-dir . --write ." 41 | }, 42 | "type": "module", 43 | "dependencies": { 44 | "@master/css": "2.0.0-rc.13" 45 | } 46 | } -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare namespace App { 3 | // interface Error {} 4 | interface Locals { 5 | surreal: import('surrealdb.js').Surreal; 6 | user: import('$types').User | undefined; 7 | } 8 | // interface PageData {} 9 | // interface Platform {} 10 | } 11 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | %sveltekit.head% 13 | 14 | 15 |
%sveltekit.body%
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import { error, redirect, type Handle } from '@sveltejs/kit'; 2 | import { sequence } from '@sveltejs/kit/hooks'; 3 | import type { User } from '$types'; 4 | import { db } from '$lib/surreal'; 5 | import { logout } from '$lib/logout'; 6 | import * as config from './master.css'; 7 | import { render } from '@master/css-server' 8 | 9 | // MasterCSS integration: https://beta.css.master.co/docs/installation 10 | const mcss = (async ({ event, resolve }) => { 11 | return await resolve(event, { 12 | transformPageChunk: ({ html }) => render(html, config.default).html 13 | }); 14 | }) satisfies Handle; 15 | 16 | // Authentication and Authorization 17 | const auth = (async ({ event, resolve }) => { 18 | const { cookies } = event; 19 | const token = cookies.get('token'); // Get token from cookie. 20 | const secureRoute = event.route.id?.includes('(user)'); // Detect protected route. 21 | const loginRoute = event.url.pathname.includes('/login'); // Detect login form. 22 | const registrationRoute = event.url.pathname.includes('/register'); // Detect registration form. 23 | const authRoute = loginRoute || registrationRoute; 24 | 25 | // If cookie with token exist and user is under protected route, authenticate user in SurrealDB. 26 | if (token && secureRoute) { 27 | const authenticated = await db.authenticate(token).catch(async (err: Error) => { 28 | console.log(`Error: ${err.message}. Session invalidation.`); 29 | // If something wrong with token - invalidate session client side, server side and in SurrealDB. 30 | await logout(event); 31 | }); 32 | if (authenticated) { 33 | // Get authenticated user info and add it to request 34 | if (!event.locals.user) { 35 | const user = (await db.info().catch((err: Error) => { 36 | console.log(`error: ${err.message}`); 37 | error(500, 'Something wrong with database connection.'); 38 | })) as User; 39 | event.locals.user = user; 40 | } 41 | } else { 42 | // If not authenticated - invalidate session client side, server side and in SurrealDB. 43 | await logout(event); 44 | } 45 | } 46 | 47 | if (secureRoute) { 48 | if (!event.locals.user || !token) { 49 | // If user is in protected route, but cookie with token or user info in request is missing - invalidate session client side, server side and in SurrealDB. 50 | await logout(event); 51 | } 52 | } 53 | if (authRoute && token) { 54 | // Prevent logged in users from navigating to login / register forms. 55 | redirect(303, '/'); 56 | } 57 | 58 | // If no problems found - let's go. 59 | const response = await resolve(event); 60 | return response; 61 | }) satisfies Handle; 62 | 63 | export const handle = sequence(mcss, auth); 64 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from 'vitest'; 2 | 3 | describe('sum test', () => { 4 | it('adds 1 + 2 to equal 3', () => { 5 | expect(1 + 2).toBe(3); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/lib/createStore.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | 3 | export function createStore(storeName: string, data: T[]) { 4 | const store = writable(data); 5 | 6 | const dynamicStore = { 7 | subscribe: store.subscribe, 8 | set: store.set, 9 | update: store.update, 10 | }; 11 | 12 | Object.defineProperty(dynamicStore, 'name', { value: storeName }); 13 | 14 | return dynamicStore; 15 | } -------------------------------------------------------------------------------- /src/lib/logout.ts: -------------------------------------------------------------------------------- 1 | import { redirect, type RequestEvent } from '@sveltejs/kit'; 2 | import { db } from './surreal'; 3 | 4 | export const logout = async (event: RequestEvent, redirect_to?: string) => { 5 | event.locals.user = undefined; 6 | event.cookies.set('token', '', { 7 | path: '/', 8 | httpOnly: true, 9 | sameSite: 'strict', 10 | secure: process.env.NODE_ENV === 'production', 11 | maxAge: -1 12 | }); 13 | await db.invalidate(); 14 | if (redirect_to) { 15 | redirect(302, redirect_to); 16 | } else { 17 | redirect(302, '/login'); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/modules/Footer.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lib/modules/Nav.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#if user} 13 |
14 |
15 | Welcome,{user.username}! 19 |
20 | {#if $page.route.id !== '/(user)'} 21 |
22 | back to Homepage 23 |
24 | {/if} 25 |
26 |
27 | 28 |
29 |
30 |
31 | {:else} 32 |
33 |
34 | Log in 35 | Create account 36 |
37 |
38 | {/if} 39 | -------------------------------------------------------------------------------- /src/lib/styles/button.mcss.ts: -------------------------------------------------------------------------------- 1 | export const mcssButton = `p:15|30 bg:yellow-50 bg:yellow-40hover cursor:pointer f:bold fg:gray-80 fg:gray-90:hover mx:20 r:3` -------------------------------------------------------------------------------- /src/lib/styles/card.mcss.ts: -------------------------------------------------------------------------------- 1 | export const card = 'bg:yellow-50 fg:gray-80 p:10 p:6@<2xs flex mt:50 as:center r:3' 2 | export const card_title = 'pt:10 px:20 px:2@<2xs font:bold font:14@<2xs white-space:nowrap white-space:normal@<2xs text:center@<2xs' 3 | export const card_stat = 'px:20 font:30 font:24@<2xs font:bold text:center@<2xs' 4 | export const card_icon = '48x48_svg 32x32_svg@<2xs p:10 p:2@<2xs as:center ' 5 | export const card_content = 'w:full' 6 | export const card_link = '' -------------------------------------------------------------------------------- /src/lib/styles/form.mcss.ts: -------------------------------------------------------------------------------- 1 | const form = 'rel flex flex:col' 2 | const field = 'flex_.field flex:col_.field p:20_.field fg:yellow-50_.field' 3 | const label = 'cursor:pointer_.field_label f:bold_.field_label' 4 | const invalid = 'p:10|5_.invalid fg:red-60_.invalid bg:red-60_.field_input.invalid b:3|solid|red-90_.field_input.invalid fg:black_.field_input.invalid' 5 | const input = `p:6_.field_input w:full_.field_input r:3_.field_input bg:gray-80_.field_input b:2|solid|gray-70_.field_input 6 | b:2|solid|yellow-50_.field_input:hover f:bold_.field_input outline:2|solid|yellow-50_.field_input:focus` //my:10_.field_input 7 | const textarea = `p:6_.field_textarea w:full_.field_textarea r:3_.field_textarea bg:gray-80_.field_textarea b:2|solid|gray-70_.field_textarea 8 | b:2|solid|yellow-50_.field_textarea:hover f:bold_.field_textarea resize:vertical_.field_textarea outline:2|solid|yellow-50_.field_textarea:focus` // my:10_.field_textarea 9 | 10 | export const mcssForm = `${form} ${field} ${label} ${invalid} ${input} ${textarea}`.replace(/\s\s+/g, ' ').trim() 11 | -------------------------------------------------------------------------------- /src/lib/surreal.ts: -------------------------------------------------------------------------------- 1 | import { PUBLIC_SURREALDB_URL } from '$env/static/public'; 2 | import { Surreal } from 'surrealdb.js'; 3 | import type {Writable } from 'svelte/store'; 4 | 5 | const MAX_RETRIES = 5; 6 | const RETRY_TIMEOUT = 2000; // 2 seconds 7 | const DB_URL = PUBLIC_SURREALDB_URL; 8 | let _db: Surreal; 9 | 10 | const database = { 11 | get instance() { 12 | if (!_db) { 13 | let retries = 1; 14 | 15 | const tryConnect = async () => { 16 | try { 17 | if (retries > 1) { 18 | console.log(`Database connection retry, attempt number ${retries} of ${MAX_RETRIES}`); 19 | } 20 | _db = new Surreal(); 21 | 22 | if (!DB_URL) return null; 23 | await _db.connect(DB_URL, { namespace: 'surreal', database: 'sveltekit' }); 24 | } catch (error) { 25 | if (retries < MAX_RETRIES) { 26 | retries++; 27 | setTimeout(tryConnect, RETRY_TIMEOUT); 28 | } else { 29 | console.log('Database connection failed.'); 30 | throw error; 31 | } 32 | } 33 | }; 34 | 35 | tryConnect(); 36 | } 37 | return _db; 38 | }, 39 | }; 40 | 41 | export const db = database.instance; 42 | 43 | export const observeLive = async >(thing: string, store: Writable) => { 44 | await db.live(thing, ({ action, result }) => { 45 | 46 | // TODO: on 'CREATE' and 'UPDATE' author (user) object is not fetched, this results in undefined in author.username 47 | switch (action) { 48 | case 'CREATE': 49 | return store.update((data) => [ result as T, ...data]); 50 | case 'UPDATE': 51 | return store.update((data) => 52 | data.map((record) => (record.id === result.id ? result as T : record)) 53 | ); 54 | case 'DELETE': 55 | return store.update((data) => data.filter((record) => record.id !== result)); 56 | case 'CLOSE': 57 | return; 58 | } 59 | }); 60 | }; 61 | -------------------------------------------------------------------------------- /src/lib/types/Post.type.ts: -------------------------------------------------------------------------------- 1 | import type { User } from './User.type'; 2 | 3 | export type Post = { 4 | id: string; 5 | title: string; 6 | slug: string; 7 | content: string; 8 | created_at: Date; 9 | updated_at?: Date; 10 | author: User; 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/types/User.type.ts: -------------------------------------------------------------------------------- 1 | export type User = { 2 | id: string; 3 | username: string; 4 | slug: string; 5 | password?: string; 6 | created_at: Date; 7 | etoro?: string; 8 | github?: string; 9 | linkedin?: string; 10 | website?: string; 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { Post } from './Post.type'; 2 | import type { User } from './User.type'; 3 | 4 | export { Post, User }; 5 | -------------------------------------------------------------------------------- /src/master.css.ts: -------------------------------------------------------------------------------- 1 | // import type { Config } from '@master/css'; 2 | // import { variables } from '@master/css'; 3 | 4 | // export default { 5 | // styles: { 6 | // body: 'bg:gray-10 fg:gray-80 f:sans f:18' 7 | // }, 8 | // colors: {}, 9 | // rules: {}, 10 | // variables: { 11 | // fontFamily: { 12 | // sans: ['akshar', ...variables.fontFamily.sans], 13 | // }, 14 | // fontWeight: { 15 | // normal: 400, 16 | // bold: 600 17 | // } 18 | // }, 19 | // semantics: {}, 20 | // mediaQueries: {}, 21 | // animations: {}, 22 | // selectors: {}, 23 | // functions: {} 24 | // } satisfies Config as Config; 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/routes/(user)/+page.server.ts: -------------------------------------------------------------------------------- 1 | import type { PageServerLoad } from './$types'; 2 | import { type Actions, redirect } from '@sveltejs/kit'; 3 | import { db } from '$lib/surreal'; 4 | import type { Post, User } from '$types'; 5 | 6 | export const load = (async ({ locals }) => { 7 | const posts = await db.query<[Post[]]>(`SELECT *, author.* FROM post ORDER BY created_at DESC`); 8 | const total_users = await db.query<[User[]]>(`SELECT * FROM user`); 9 | 10 | // const latest_user = await db.query[]>(`SELECT * FROM $latest_user`); 11 | const latest_user = await db.query<[User[]]>(`SELECT * FROM user ORDER BY created_at DESC LIMIT 1`) 12 | // const your_posts = await db.query('SELECT *, author.* FROM your_posts'); 13 | const your_posts = await db.query<[Post[]]>('SELECT *, author.* FROM post WHERE author == $author', { 14 | author: locals.user?.id 15 | }); 16 | 17 | return { 18 | locals, 19 | posts: posts[0] as Post[], 20 | latest_user: latest_user[0] as User[], 21 | your_posts: your_posts[0] as Post[], 22 | total_users: total_users[0] as User[] 23 | }; 24 | }) satisfies PageServerLoad; 25 | 26 | export const actions = { 27 | logout: async (event) => { 28 | event.locals.user = undefined; 29 | event.cookies.set('token', '', { 30 | path: '/', 31 | httpOnly: true, 32 | sameSite: 'strict', 33 | secure: process.env.NODE_ENV === 'production', 34 | maxAge: -1 35 | }); 36 | 37 | await db.invalidate(); 38 | redirect(303, '/'); 39 | } 40 | } satisfies Actions; 41 | -------------------------------------------------------------------------------- /src/routes/(user)/+page.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 |
35 | Add post 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
Total users
47 |
{$total_users.length}
48 |
49 |
50 |
51 |
52 |
53 |
Total posts
54 |
{$posts.length}
55 |
56 |
57 | 58 |
59 |
60 |
61 |
Your posts
62 |
{$your_posts.length}
63 |
64 |
65 |
66 |
67 |
68 |
Latest user:
69 |
{$latest_user[0].username}

joined {dayjs($latest_user[0].created_at).fromNow()}

70 |
71 |
72 | 73 |

Latest posts

74 | 75 | {#if $posts.length > 0} 76 | {#each $posts as post, i (post.id)} 77 |
79 |
80 |
81 | Written {dayjs(post.created_at).fromNow()} 82 | by {post.author.username} 83 |
84 |
85 |

{post.title}

86 |
87 | {post.content} 88 |
89 |
90 | 91 | {/each} 92 | {:else} 93 | No posts yet 94 | {/if} 95 |
96 |
97 | -------------------------------------------------------------------------------- /src/routes/(user)/author/[author_slug]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { db } from '$lib/surreal'; 2 | import { error } from '@sveltejs/kit'; 3 | import type { PageServerLoad } from './$types'; 4 | import type { User } from '$types'; 5 | 6 | export const load = (async ({ locals, params }) => { 7 | const author = await db.query<[User[]]>('SELECT * FROM user WHERE slug = $slug LIMIT 1', { 8 | slug: params.author_slug 9 | }) 10 | if (!author) error(404, `This author is not registered yet.`); 11 | 12 | return { locals, author: author[0][0] as User }; 13 | }) satisfies PageServerLoad; 14 | -------------------------------------------------------------------------------- /src/routes/(user)/author/[author_slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 |

{data.author?.username}

19 |

Joined {dayjs(data.author?.created_at).fromNow()}

20 |
21 | 22 | {#if data.author?.website} 23 |

Website: {data.author?.website}

24 | {/if} 25 | {#if data.author?.linkedin} 26 |

LinkedIn: Connect on LinkedIn

27 | {/if} 28 | {#if data.author?.github} 29 |

GitHub: Check GitHub profile

30 | {/if} 31 | 32 |
33 | {#if data.author?.etoro} 34 |

Copy investment portfolio on eToro: {data.author?.etoro}

35 | {/if} 36 | 37 | 38 |
39 |
40 | 41 |
-------------------------------------------------------------------------------- /src/routes/(user)/post/[post_slug]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { fail, redirect } from '@sveltejs/kit'; 2 | import type { Actions, PageServerLoad } from './$types'; 3 | import { db } from '$lib/surreal'; 4 | import type { Post } from '$types'; 5 | import { setMessage, superValidate } from 'sveltekit-superforms/server'; 6 | import { schema } from './schema'; 7 | 8 | export const load = (async ({ params, locals }) => { 9 | const form = await superValidate(schema); 10 | const post = await db.query<[Post[]]>('SELECT *, author.* FROM post WHERE slug = $slug LIMIT 1', { 11 | slug: params.post_slug 12 | }); 13 | return { form, locals, post: post[0][0] as Post }; 14 | }) satisfies PageServerLoad; 15 | 16 | export const actions = { 17 | remove: async ({request, locals}) => { 18 | const user = locals.user; 19 | if (!user) redirect(303, '/login'); 20 | 21 | const form = await superValidate(request, schema); 22 | 23 | if (!form.valid) { 24 | return fail(400, { form }); 25 | } 26 | const { post_id, author_id } = form.data 27 | 28 | if (author_id !== locals.user?.id) { 29 | setMessage(form, `You are not the author of this post. You can't delete it.`) 30 | return fail(400, { form }) 31 | } 32 | 33 | await db.query('DELETE $post', { 34 | post: post_id 35 | }).catch((e: Error) => { 36 | console.log(e) 37 | }) 38 | 39 | 40 | 41 | redirect(303, '/'); 42 | } 43 | } satisfies Actions; -------------------------------------------------------------------------------- /src/routes/(user)/post/[post_slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
19 | {#if $message} 20 |
21 | {$message} 22 |
23 | {/if} 24 |
25 |
26 | {#if data.post?.author.id === data.locals.user?.id} 27 |
EDIT | 28 |
29 | 30 | 31 | 32 |
33 |
34 | {/if} 35 |
36 | Written {dayjs(data.post?.created_at).fromNow()} 37 | by {data.post?.author.username} 38 |
39 |
40 |

{data.post?.title}

41 |
42 | {data.post?.content} 43 |
44 |
45 | 46 |
-------------------------------------------------------------------------------- /src/routes/(user)/post/[post_slug]/edit/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { error, fail, redirect } from '@sveltejs/kit'; 2 | import type { Actions, PageServerLoad } from './$types'; 3 | import { db } from '$lib/surreal'; 4 | import type { Post } from '$types'; 5 | import { setError, superValidate } from 'sveltekit-superforms/server'; 6 | import { schema } from './schema'; 7 | 8 | export const load = (async ({ locals, params }) => { 9 | const post = await db.query<[Post[]]>('SELECT *, author.* FROM post WHERE slug = $slug LIMIT 1', { 10 | slug: params.post_slug 11 | }); 12 | const form = await superValidate(post[0][0] as Post, schema); 13 | return { form, locals, post: post[0][0] as Post }; 14 | }) satisfies PageServerLoad; 15 | 16 | export const actions = { 17 | edit: async ({ request, locals, params }) => { 18 | const user = locals.user; 19 | if (!user) redirect(303, '/login'); 20 | const form = await superValidate(request, schema); 21 | 22 | if (!form.valid) { 23 | return fail(400, { form }); 24 | } 25 | 26 | const { author_id, post_id, title, content } = form.data; 27 | if (author_id !== locals.user?.id) { 28 | error(403, `You are not a Russian propagandist, you can't modify other people's posts.`); 29 | } 30 | let isError = false 31 | try { 32 | await db 33 | .merge(post_id, { 34 | title: title, 35 | content: content 36 | }) 37 | .catch((e: Error) => { 38 | if (e.message.includes('title' && title)) { 39 | isError = true 40 | return setError( 41 | form, 42 | 'title', 43 | `The title "${title}" is already in use. Please come up with something else.` 44 | ); 45 | } else { 46 | console.log(e.message); 47 | } 48 | }) 49 | } catch (e) { 50 | const err = e as Error; 51 | error(500, `Error: ${err.message}`); 52 | } 53 | 54 | if (!isError) redirect(303, `/post/${params.post_slug}`); 55 | return { form } 56 | } 57 | } satisfies Actions; 58 | -------------------------------------------------------------------------------- /src/routes/(user)/post/[post_slug]/edit/+page.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | {#if $message} 22 |
23 | {$message} 24 |
25 | {/if} 26 | {#if data.post?.author.id === data.locals.user?.id} 27 |
33 | {#if $message} 34 |
{$message}
35 | {/if} 36 | 37 | 38 |
39 | 40 | 41 | {#if $errors.title}{$errors.title}{/if} 42 |
43 |
44 | 45 |