├── .env.default ├── .eslintrc.json ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── server-CI.yaml ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── components.json ├── docker-compose.yml ├── jest.config.js ├── next-env.d.ts ├── next.config.mjs ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── assets │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── favicon.png │ ├── favicon.svg │ ├── logo.png │ ├── logo.svg │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── mstile-70x70.png │ └── safari-pinned-tab.svg └── site.webmanifest ├── renovate.json ├── src ├── .dockerignore ├── Dockerfile ├── app │ ├── (drift) │ │ ├── (auth) │ │ │ ├── components │ │ │ │ ├── auth.module.css │ │ │ │ ├── index.tsx │ │ │ │ └── query-handler.tsx │ │ │ ├── signin │ │ │ │ └── page.tsx │ │ │ └── signup │ │ │ │ └── page.tsx │ │ ├── (posts) │ │ │ ├── components │ │ │ │ ├── document-tabs │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── tabs.module.css │ │ │ │ ├── file-dropdown │ │ │ │ │ ├── dropdown.module.css │ │ │ │ │ └── index.tsx │ │ │ │ └── preview │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── katex.min.css │ │ │ │ │ └── preview.module.css │ │ │ ├── expired │ │ │ │ └── page.tsx │ │ │ ├── new │ │ │ │ ├── components │ │ │ │ │ ├── description │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── drag-and-drop │ │ │ │ │ │ ├── drag-and-drop.module.css │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── edit-document-list │ │ │ │ │ │ ├── edit-document │ │ │ │ │ │ │ ├── document.module.css │ │ │ │ │ │ │ ├── formatting-icons │ │ │ │ │ │ │ │ ├── formatting-icons.module.css │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── new.tsx │ │ │ │ │ ├── post.module.css │ │ │ │ │ └── title │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── title.module.css │ │ │ │ ├── from │ │ │ │ │ └── [id] │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ │ └── post │ │ │ │ └── [id] │ │ │ │ ├── components │ │ │ │ ├── header │ │ │ │ │ ├── post-buttons │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── post-buttons.module.css │ │ │ │ │ └── title │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── title.module.css │ │ │ │ └── post-files │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── password-modal-wrapper.tsx │ │ │ │ │ └── view-document │ │ │ │ │ ├── document.module.css │ │ │ │ │ └── index.tsx │ │ │ │ ├── context.tsx │ │ │ │ ├── file │ │ │ │ └── raw │ │ │ │ │ └── [title] │ │ │ │ │ └── route.ts │ │ │ │ ├── get-post.tsx │ │ │ │ ├── layout.module.css │ │ │ │ ├── layout.tsx │ │ │ │ ├── loading.tsx │ │ │ │ └── page.tsx │ │ ├── admin │ │ │ ├── components │ │ │ │ ├── table.module.css │ │ │ │ └── tables.tsx │ │ │ ├── layout.tsx │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── author │ │ │ └── [username] │ │ │ │ └── page.tsx │ │ ├── home │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── mine │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── providers.tsx │ │ └── settings │ │ │ ├── components │ │ │ └── sections │ │ │ │ ├── api-keys.module.css │ │ │ │ ├── api-keys.tsx │ │ │ │ ├── profile.module.css │ │ │ │ └── profile.tsx │ │ │ ├── loading.tsx │ │ │ └── page.tsx │ ├── api │ │ └── auth │ │ │ ├── [...nextauth] │ │ │ └── route.ts │ │ │ └── requires-passcode │ │ │ └── route.ts │ ├── components │ │ ├── alert-dialog.tsx │ │ ├── badges │ │ │ ├── badge.module.css │ │ │ ├── badge.tsx │ │ │ ├── created-ago-badge │ │ │ │ └── index.tsx │ │ │ ├── expiration-badge │ │ │ │ └── index.tsx │ │ │ ├── visibility-badge │ │ │ │ └── index.tsx │ │ │ └── visibility-control │ │ │ │ └── index.tsx │ │ ├── button-dropdown │ │ │ ├── dropdown.module.css │ │ │ └── index.tsx │ │ ├── button-group │ │ │ ├── button-group.module.css │ │ │ └── index.tsx │ │ ├── button │ │ │ └── index.tsx │ │ ├── calendar.tsx │ │ ├── card │ │ │ ├── card.module.css │ │ │ └── index.tsx │ │ ├── cmdk │ │ │ ├── cmdk.tsx │ │ │ ├── dialog.css │ │ │ ├── index.tsx │ │ │ ├── item.tsx │ │ │ └── pages │ │ │ │ ├── home.tsx │ │ │ │ └── posts.tsx │ │ ├── date-picker.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu │ │ │ └── index.tsx │ │ ├── error │ │ │ └── fallback.tsx │ │ ├── fade-in │ │ │ ├── fade.module.css │ │ │ └── index.tsx │ │ ├── header │ │ │ ├── buttons.tsx │ │ │ ├── header.module.css │ │ │ ├── index.tsx │ │ │ └── mobile.tsx │ │ ├── input │ │ │ ├── index.tsx │ │ │ └── input.module.css │ │ ├── layout │ │ │ ├── index.tsx │ │ │ └── page.module.css │ │ ├── link │ │ │ ├── index.tsx │ │ │ └── link.module.css │ │ ├── navigation-menu.tsx │ │ ├── note │ │ │ ├── index.tsx │ │ │ └── note.module.css │ │ ├── page-title.tsx │ │ ├── page-wrapper.tsx │ │ ├── password-modal │ │ │ ├── index.tsx │ │ │ └── modal.module.css │ │ ├── popover │ │ │ ├── index.tsx │ │ │ └── popover.module.css │ │ ├── post-list │ │ │ ├── index.tsx │ │ │ ├── list-item-skeleton.tsx │ │ │ ├── list-item.module.css │ │ │ ├── list-item.tsx │ │ │ └── post-list.module.css │ │ ├── scroll-to-top │ │ │ ├── index.tsx │ │ │ └── scroll.module.css │ │ ├── settings-group │ │ │ ├── index.tsx │ │ │ └── settings-group.module.css │ │ ├── skeleton │ │ │ └── index.tsx │ │ ├── spinner │ │ │ ├── index.tsx │ │ │ └── spinner.module.css │ │ ├── stack │ │ │ └── index.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── toasts │ │ │ └── index.tsx │ │ ├── tooltip │ │ │ ├── index.tsx │ │ │ └── tooltip.module.css │ │ └── typography │ │ │ └── index.tsx │ ├── hooks │ │ ├── swr │ │ │ └── use-api-tokens.ts │ │ ├── use-debounce.ts │ │ └── use-trace-route.ts │ ├── lib │ │ ├── copy-to-clipboard.ts │ │ ├── fetch-with-user.ts │ │ ├── get-title-for-post-copy.ts │ │ ├── get-url-friendly-title.tsx │ │ ├── metadata.tsx │ │ └── time-ago.ts │ ├── pages │ │ └── [fileId] │ │ │ └── [fileTitle] │ │ │ ├── layout.tsx │ │ │ ├── page.tsx │ │ │ └── theme-provider.tsx │ └── styles │ │ ├── globals.css │ │ ├── markdown.css │ │ └── syntax.css ├── docker-compose.yml ├── lib │ ├── __tests__ │ │ └── byte-to-mb.test.ts │ ├── api-middleware │ │ └── with-methods.ts │ ├── byte-to-mb.ts │ ├── cn.ts │ ├── config.ts │ ├── constants.ts │ ├── generate-uuid.ts │ ├── gist │ │ ├── __tests__-backup │ │ │ └── index.ts │ │ ├── fetch.ts │ │ ├── index.ts │ │ ├── transform.ts │ │ └── types.d.ts │ ├── revalidate-page.ts │ ├── server │ │ ├── __tests__ │ │ │ └── verify-api-user.test.ts │ │ ├── auth-props.ts │ │ ├── auth.ts │ │ ├── get-html-from-drift-file.ts │ │ ├── parse-query-param.ts │ │ ├── prisma.ts │ │ ├── session.ts │ │ └── verify-api-user.ts │ └── use-session-swr.ts ├── middleware.ts ├── pages │ └── api │ │ ├── admin │ │ └── index.ts │ │ ├── file │ │ ├── get-html.ts │ │ ├── html │ │ │ └── [id].ts │ │ └── raw │ │ │ └── [id].ts │ │ ├── og.tsx │ │ ├── post │ │ ├── [id].ts │ │ ├── index.ts │ │ └── search.ts │ │ ├── revalidate.ts │ │ ├── user │ │ ├── [userId] │ │ │ ├── index.ts │ │ │ └── posts │ │ │ │ └── index.ts │ │ └── tokens.ts │ │ └── welcome.ts ├── prisma │ ├── migrations │ │ └── migration_lock.toml │ └── schema.prisma └── test │ ├── .env.test │ ├── prisma.mock.ts │ ├── react.mock.ts │ └── setup-tests.ts ├── tailwind.config.js ├── tools └── upload.sh ├── tsconfig.json └── types ├── index.d.ts └── next-auth.d.ts /.env.default: -------------------------------------------------------------------------------- 1 | DATABASE_URL=postgresql://user:password@localhost:5432/dbname 2 | 3 | # Optional if you use Vercel (defaults to VERCEL_URL). 4 | # Necessary in development unless you use the vercel CLI (`vc dev`) 5 | DRIFT_URL=http://localhost:3000 6 | 7 | # Optional: The first user becomes an admin. Defaults to false 8 | ENABLE_ADMIN=false 9 | 10 | # Required: Next auth secret is a required valid JWT secret. You can generate one with `openssl rand -hex 32` 11 | NEXTAUTH_SECRET=7f8b8b5c5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5e5f5f5 12 | 13 | # Required: but unnecessary if you use a supported host like Vercel 14 | NEXTAUTH_URL=http://localhost:3000 15 | 16 | # Optional: for locking your instance 17 | REGISTRATION_PASSWORD= 18 | 19 | # Optional: for if you want GitHub oauth. Currently incompatible with the registration password 20 | GITHUB_CLIENT_ID= 21 | GITHUB_CLIENT_SECRET= 22 | 23 | # Optional: if you want Keycloak oauth. Currently incompatible with the registration password 24 | KEYCLOAK_ID= 25 | KEYCLOAK_SECRET= 26 | KEYCLOAK_ISSUER= # keycloak path including realm 27 | KEYCLOAK_NAME= 28 | 29 | # Optional: if you want to support credential auth (username/password, supports registration password) 30 | # Defaults to true 31 | CREDENTIAL_AUTH=true 32 | 33 | # Optional: 34 | WELCOME_CONTENT= 35 | WELCOME_TITLE= 36 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "plugin:@typescript-eslint/recommended"], 3 | "ignorePatterns": [ 4 | "node_modules/", 5 | "__tests__/", 6 | "coverage/", 7 | ".next/", 8 | "public" 9 | ], 10 | "rules": { 11 | "@typescript-eslint/no-unused-vars": "error", 12 | "@typescript-eslint/no-explicit-any": "error" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: MaxLeiter 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: Feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/server-CI.yaml: -------------------------------------------------------------------------------- 1 | name: Server CI 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'server/**' 7 | - '.github/workflows/**' 8 | 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | defaults: 14 | run: 15 | working-directory: server 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup node 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: '16' 23 | - name: Install deps 24 | run: yarn 25 | - name: Run tests 26 | run: yarn test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | analyze 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | .pnpm-debug.log* 28 | 29 | # production env 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "none", 4 | "singleQuote": false, 5 | "printWidth": 80, 6 | "useTabs": true, 7 | "plugins": ["prettier-plugin-tailwindcss"] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/.pnpm/typescript@4.9.4/node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true, 4 | "dotenv.enableAutocloaking": false 5 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Thank you for your interest in Drift! 4 | 5 | ### I want to report a bug 6 | 7 | Look at the open and closed issues to see if this was not already discussed before. If you can't see any, feel free to open a new issue. 8 | If you think you discovered a security vulnerability, do not open a public issue on GitHub. Please email maxwell.leiter@gmail.com in the interest of responsible disclosure. 9 | 10 | ### I want to contribute to the code 11 | 12 | Make sure to discuss your ideas with the community in an issue or on the IRC channel. 13 | Take a look at the open issues labeled as help wanted or good first issue if you want to help without having a specific idea in mind. 14 | Make sure that your PRs do not contain unnecessary commits or merge commits. Squash commits whenever possible. 15 | Rebase (instead of merge) outdated PRs on the master branch. 16 | Give extra care to your commit messages. Use the imperative present tense and follow Tim Pope's guidelines. 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Max Leiter 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. 22 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@components", 14 | "utils": "@utils" 15 | } 16 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | server: 3 | build: 4 | context: ./server 5 | args: 6 | - NODE_ENV=production 7 | container_name: server 8 | restart: unless-stopped 9 | user: 1000:1000 10 | environment: 11 | - PORT 12 | - JWT_SECRET=jwt_secret # change_me! # use `openssl rand -hex 32` to generate a strong secret 13 | - SECRET_KEY=secret # change me! 14 | - MEMORY_DB 15 | - REGISTRATION_PASSWORD 16 | - WELCOME_CONTENT 17 | - WELCOME_TITLE 18 | - ENABLE_ADMIN 19 | - DRIFT_HOME 20 | ports: 21 | - "3000:3000" 22 | client: 23 | build: 24 | context: ./client 25 | args: 26 | - API_URL=http://server:3000 27 | container_name: client 28 | restart: unless-stopped 29 | user: 1000:1000 30 | environment: 31 | - API_URL=http://server:3000 32 | - SECRET_KEY=secret # change me! 33 | ports: 34 | - "3001:3001" 35 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: "ts-jest", 4 | testEnvironment: "node", 5 | setupFiles: ["/src/test/setup-tests.ts"], 6 | // TODO: update to app dir 7 | moduleNameMapper: { 8 | "@lib/(.*)": "/src/lib/$1", 9 | "@components/(.*)": "/src/app/components/$1", 10 | "\\.(css)$": "identity-obj-proxy" 11 | }, 12 | testPathIgnorePatterns: ["/node_modules/", "/.next/"], 13 | transform: { 14 | "^.+\\.(js|jsx|ts|tsx)$": "ts-jest" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | // NOTE: This file should not be edited 6 | // see https://nextjs.org/docs/basic-features/typescript for more information. 7 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | import bundleAnalyzer from "@next/bundle-analyzer" 2 | 3 | /** @type {import('next').NextConfig} */ 4 | const nextConfig = { 5 | reactStrictMode: true, 6 | experimental: { 7 | appDir: true 8 | }, 9 | rewrites() { 10 | return [ 11 | { 12 | source: "/file/raw/:id", 13 | destination: `/api/raw/:id` 14 | }, 15 | { 16 | source: "/signout", 17 | destination: `/api/auth/signout` 18 | } 19 | ] 20 | }, 21 | images: { 22 | domains: ["avatars.githubusercontent.com"] 23 | }, 24 | env: { 25 | NEXT_PUBLIC_DRIFT_URL: 26 | process.env.DRIFT_URL || 27 | (process.env.VERCEL_URL 28 | ? `https://${process.env.VERCEL_URL}` 29 | : "http://localhost:3000") 30 | }, 31 | eslint: { 32 | ignoreDuringBuilds: process.env.VERCEL_ENV !== "production" 33 | }, 34 | typescript: { 35 | ignoreBuildErrors: process.env.VERCEL_ENV !== "production" 36 | }, 37 | modularizeImports: { 38 | "react-feather": { 39 | transform: "react-feather/dist/icons/{{kebabCase member}}" 40 | } 41 | } 42 | } 43 | 44 | export default process.env.ANALYZE === "true" 45 | ? bundleAnalyzer({ enabled: true })(nextConfig) 46 | : nextConfig 47 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "@tailwindcss/nesting": {}, 4 | tailwindcss: {}, 5 | autoprefixer: {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /public/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /public/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/favicon-16x16.png -------------------------------------------------------------------------------- /public/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/favicon-32x32.png -------------------------------------------------------------------------------- /public/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/favicon.ico -------------------------------------------------------------------------------- /public/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/favicon.png -------------------------------------------------------------------------------- /public/assets/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/logo.png -------------------------------------------------------------------------------- /public/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/assets/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/mstile-144x144.png -------------------------------------------------------------------------------- /public/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/mstile-150x150.png -------------------------------------------------------------------------------- /public/assets/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/mstile-310x150.png -------------------------------------------------------------------------------- /public/assets/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/mstile-310x310.png -------------------------------------------------------------------------------- /public/assets/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaxLeiter/Drift/0c20460c1315f72c7dc20e0fd25f09355d4e677f/public/assets/mstile-70x70.png -------------------------------------------------------------------------------- /public/assets/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Drift", 3 | "short_name": "Drift", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base", "group:allNonMajor", "schedule:earlyMondays"] 3 | } 4 | -------------------------------------------------------------------------------- /src/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | README.md 6 | .next 7 | .git 8 | .env.* -------------------------------------------------------------------------------- /src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:17-alpine AS deps 2 | 3 | # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. 4 | RUN apk add --no-cache libc6-compat 5 | 6 | WORKDIR /app 7 | 8 | COPY package.json yarn.lock ./ 9 | 10 | RUN yarn install --frozen-lockfile 11 | 12 | FROM node:17-alpine AS builder 13 | 14 | WORKDIR /app 15 | 16 | COPY --from=deps /app/node_modules ./node_modules 17 | COPY . . 18 | 19 | ARG API_URL 20 | 21 | ENV NEXT_TELEMETRY_DISABLED=1 22 | ENV API_URL=${API_URL:-http://localhost:3000} 23 | 24 | RUN yarn build 25 | 26 | FROM node:17-alpine AS runner 27 | 28 | WORKDIR /app 29 | 30 | ARG NODE_ENV 31 | 32 | ENV NEXT_TELEMETRY_DISABLED=1 33 | ENV NODE_ENV=${NODE_ENV:-production} 34 | 35 | RUN addgroup --system --gid 1001 nodejs 36 | RUN adduser --system --uid 1001 nextjs 37 | 38 | COPY --from=builder /app/next.config.mjs ./ 39 | COPY --from=builder /app/public ./public 40 | COPY --from=builder /app/package.json ./package.json 41 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ 42 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static 43 | 44 | USER nextjs 45 | 46 | ENV PORT=3001 47 | 48 | EXPOSE 3001 49 | 50 | CMD ["node", "server.js"] 51 | -------------------------------------------------------------------------------- /src/app/(drift)/(auth)/components/auth.module.css: -------------------------------------------------------------------------------- 1 | .formGroup { 2 | display: flex; 3 | flex-direction: column; 4 | place-items: center; 5 | gap: 10px; 6 | } 7 | 8 | .formContentSpace { 9 | margin-bottom: 1rem; 10 | text-align: center; 11 | } 12 | -------------------------------------------------------------------------------- /src/app/(drift)/(auth)/components/query-handler.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useToasts } from "@components/toasts" 4 | import { useSearchParams } from "next/navigation" 5 | import { Suspense, useEffect } from "react" 6 | 7 | function InnerErrorQueryParamsHandler() { 8 | const queryParams = useSearchParams() 9 | const { setToast } = useToasts() 10 | 11 | useEffect(() => { 12 | if (queryParams?.get("error")) { 13 | setToast({ 14 | message: queryParams.get("error") as string, 15 | type: "error" 16 | }) 17 | } 18 | }, [queryParams, setToast]) 19 | 20 | return null 21 | } 22 | 23 | export function ErrorQueryParamsHandler() { 24 | /* Suspense boundary because useSearchParams causes static bailout */ 25 | return ( 26 | 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/app/(drift)/(auth)/signin/page.tsx: -------------------------------------------------------------------------------- 1 | import { getMetadata } from "src/app/lib/metadata" 2 | 3 | import Auth from "../components" 4 | import { getAuthProviders, isCredentialEnabled } from "@lib/server/auth-props" 5 | import { PageWrapper } from "@components/page-wrapper" 6 | 7 | export default function SignInPage() { 8 | return ( 9 | 10 | 15 | 16 | ) 17 | } 18 | 19 | export const metadata = getMetadata({ 20 | title: "Sign in" 21 | }) 22 | -------------------------------------------------------------------------------- /src/app/(drift)/(auth)/signup/page.tsx: -------------------------------------------------------------------------------- 1 | import Auth from "../components" 2 | import { getMetadata } from "src/app/lib/metadata" 3 | import { getAuthProviders, isCredentialEnabled } from "@lib/server/auth-props" 4 | import { getRequiresPasscode } from "src/app/api/auth/requires-passcode/route" 5 | import { PageWrapper } from "@components/page-wrapper" 6 | 7 | async function getPasscode() { 8 | return getRequiresPasscode() 9 | } 10 | 11 | export default async function SignUpPage() { 12 | const requiresPasscode = await getPasscode() 13 | return ( 14 | 15 | 21 | 22 | ) 23 | } 24 | 25 | export const metadata = getMetadata({ 26 | title: "Sign up" 27 | }) 28 | -------------------------------------------------------------------------------- /src/app/(drift)/(posts)/components/document-tabs/index.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import FormattingIcons from "src/app/(drift)/(posts)/new/components/edit-document-list/edit-document/formatting-icons" 4 | import { 5 | ChangeEvent, 6 | ClipboardEvent, 7 | ComponentProps, 8 | useRef, 9 | useState 10 | } from "react" 11 | import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor" 12 | import Preview, { StaticPreview } from "../preview" 13 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/tabs" 14 | import { Textarea } from "@components/textarea" 15 | 16 | type Props = ComponentProps & { 17 | isEditing: boolean 18 | defaultTab: "preview" | "edit" 19 | handleOnContentChange?: (e: ChangeEvent) => void 20 | onPaste?: (e: ClipboardEvent) => void 21 | title?: string 22 | staticPreview?: string 23 | children: string 24 | } 25 | 26 | export default function DocumentTabs({ 27 | isEditing, 28 | defaultTab, 29 | handleOnContentChange, 30 | onPaste, 31 | title, 32 | staticPreview: preview, 33 | children: rawContent, 34 | ...props 35 | }: Props) { 36 | const codeEditorRef = useRef(null) 37 | const [activeTab, setActiveTab] = useState<"preview" | "edit">(defaultTab) 38 | const handleTabChange = (newTab: string) => { 39 | if (newTab === "preview") { 40 | codeEditorRef.current?.focus() 41 | } 42 | setActiveTab(newTab as "preview" | "edit") 43 | } 44 | 45 | return ( 46 | 47 | 48 |
49 | {isEditing ? "Edit" : "Raw"} 50 | 51 | {isEditing ? "Preview" : "Rendered"} 52 | 53 |
54 | {isEditing && ( 55 | 61 | )} 62 |
63 | 64 |
71 | 79 | 80 |