3 |
4 | Cusdis is an open-source, lightweight (~5kb gzip), privacy-friendly alternative to Disqus.
5 |
6 | > Contact me if you want to buy/acquire this project 💖
7 |
8 | 
9 |
10 | [](https://railway.app/new/template?template=https%3A%2F%2Fgithub.com%2Fdjyde%2Fcusdis&plugins=postgresql&envs=NEXTAUTH_URL%2CDB_TYPE%2CDB_URL%2CUSERNAME%2CPASSWORD%2CHOST%2CJWT_SECRET%2CPORT&NEXTAUTH_URLDesc=Don%27t+modify+it&DB_TYPEDesc=Don%27t+modify+it&DB_URLDesc=Don%27t+modify+it&USERNAMEDesc=Username+to+sign+in&PASSWORDDesc=Password+to+sign+in&HOSTDesc=Don%27t+modify+it&JWT_SECRETDesc=A+secret+key+to+encrypt+JWT+token&PORTDesc=Don%27t+modify+it&NEXTAUTH_URLDefault=%24%7B%7B+RAILWAY_STATIC_URL+%7D%7D&DB_TYPEDefault=pgsql&DB_URLDefault=%24%7B%7B+DATABASE_URL+%7D%7D&HOSTDefault=https%3A%2F%2F%24%7B%7B+RAILWAY_STATIC_URL+%7D%7D&PORTDefault=3000&referralCode=randyloop)
11 |
12 | ## 💝 Sponsor this project
13 |
14 | If you like Cusdis, please consider sponsoring us to help us be sustainable.
15 |
16 | ### Principle Sponsor
17 |
18 | [](https://epubkit.app)
19 |
20 |
21 | [](https://opencollective.com/cusdis/tiers/organization-support/0/website)
22 |
23 | [Become a principle sponsor](https://opencollective.com/cusdis/contribute/organization-support-27992/checkout)
24 |
25 | ### Sponsors / Backers
26 |
27 | [](https://opencollective.com/cusdis)
28 | [](https://opencollective.com/cusdis)
29 |
30 | ## Features
31 |
32 | - Lightweight comment widget, with i18n, dark mode.
33 | - Email notification
34 | - Webhook
35 | - Easy to self-host
36 | - Many integrations
37 |
38 | ## Documentation
39 |
40 | https://cusdis.com/doc
41 |
42 | ## Community
43 |
44 | [Discord](https://discord.gg/eDs5fc4Jcq)
45 |
46 | ## FAQ
47 |
48 | ## Compared to Disqus
49 |
50 | Cusdis is not designed to be a complete alternative to Disqus. It's aim is to implement a minimalist and embeddable comment system for small websites (such as your static blog).
51 |
52 | Given below are the pros and cons of Cusdis:
53 |
54 | ### Pros
55 |
56 | - Cusdis is open-source and self-hostable. Hence, you own your data.
57 | - The SDK is lightweight(~5kb gzipped).
58 | - Cusdis doesn't require your user to sign in to make a comment.
59 | - Cusdis doesn't use cookies at all.
60 |
61 | ### Cons
62 |
63 | - Cusdis is on the early stages of its development.
64 | - There is no spam filter, hence, you will have to manually moderate your comment section and comments won't be displayed until you approve them.
65 | - Disqus is a company, we aren't.
66 |
67 | ## Contributing
68 |
69 | [Contributing Guide](https://cusdis.com/doc#/contributing)
70 |
71 | If you are going to make a PR, remember to choose `dev` as the base branch.
72 |
73 | # License
74 |
75 | GNU GPLv3
76 |
--------------------------------------------------------------------------------
/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import { Anchor, Box, Group, List, Stack, Text, Image, Center } from '@mantine/core'
2 | import * as React from 'react'
3 | import { VERSION } from '../utils.client'
4 |
5 | export function Footer(props: {
6 | maxWidth?: string
7 | }) {
8 | return (
9 |
10 |
20 | )
21 | }
22 |
23 | export async function getServerSideProps(ctx) {
24 |
25 | const session = await getSession(ctx.req)
26 |
27 | if (!session) {
28 | return {
29 | redirect: {
30 | destination: '/api/auth/signin',
31 | permanent: false,
32 | }
33 | }
34 | }
35 |
36 | const projectService = new ProjectService(ctx.req)
37 |
38 | const defaultProject = await projectService.getFirstProject(session.uid, {
39 | select: {
40 | id: true
41 | }
42 | })
43 |
44 | if (!defaultProject) {
45 | return {
46 | redirect: {
47 | destination: `/getting-start`,
48 | permanent: false
49 | }
50 | }
51 | } else {
52 | // redirect to project dashboard
53 | return {
54 | redirect: {
55 | destination: `/dashboard/project/${defaultProject.id}`,
56 | permanent: false
57 | }
58 | }
59 | }
60 | }
61 |
62 | export default Dashboard
63 |
--------------------------------------------------------------------------------
/pages/error.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from '@mantine/core'
2 |
3 | export enum ErrorCode {
4 | INVALID_TOKEN = 'INVALID_TOKEN'
5 | }
6 |
7 | function ErrorPage({
8 | errorCode
9 | }: {
10 | errorCode: ErrorCode | null
11 | }) {
12 |
13 | const info = (() => {
14 | switch (errorCode) {
15 | case ErrorCode.INVALID_TOKEN:
16 | return Invalid Token
17 | default:
18 | return Something went wrong
19 | }
20 | })()
21 |
22 | return (
23 | <>
24 | {info}
25 | >
26 | )
27 | }
28 |
29 | export async function getServerSideProps(ctx) {
30 | return {
31 | props: {
32 | errorCode: ctx.query.code || null
33 | }
34 | }
35 | }
36 |
37 | export default ErrorPage
--------------------------------------------------------------------------------
/pages/forbidden.tsx:
--------------------------------------------------------------------------------
1 | function ForbiddenPage() {
2 | return (
3 | <>
4 | Forbidden
5 | >
6 | )
7 | }
8 |
9 | export async function getServerSideProps(ctx) {
10 | return {
11 | props: {
12 |
13 | }
14 | }
15 | }
16 |
17 | export default ForbiddenPage
--------------------------------------------------------------------------------
/pages/getting-start.tsx:
--------------------------------------------------------------------------------
1 | import { Box, Container, Stack, Title, Text, Button, TextInput, Image } from "@mantine/core"
2 | import { notifications } from "@mantine/notifications"
3 | import router from "next/router"
4 | import React from "react"
5 | import { useMutation } from "react-query"
6 | import { Head } from "../components/Head"
7 | import { ProjectService } from "../service/project.service"
8 | import { apiClient } from "../utils.client"
9 | import { getSession } from "../utils.server"
10 |
11 | export const createProject = async (body: { title: string }) => {
12 | const res = await apiClient.post("/projects", {
13 | title: body.title,
14 | })
15 | return res.data
16 | }
17 |
18 | function GettingStart() {
19 | const createProjectMutation = useMutation(createProject)
20 | const titleInputRef = React.useRef(null)
21 |
22 |
23 | async function onClickCreateProject() {
24 | if (!titleInputRef.current.value) {
25 | return
26 | }
27 |
28 | await createProjectMutation.mutate(
29 | {
30 | title: titleInputRef.current.value,
31 | },
32 | {
33 | onSuccess(data) {
34 | notifications.show({
35 | title: "Project created",
36 | message: "Redirecting to project dashboard",
37 | color: 'green'
38 | })
39 | router.push(`/dashboard/project/${data.data.id}`, null, {
40 | shallow: true,
41 | })
42 | },
43 | onError(data: any) {
44 | const {
45 | error: message,
46 | status: statusCode
47 | } = data.response.data
48 |
49 | notifications.show({
50 | title: "Error",
51 | message,
52 | color: 'yellow'
53 | })
54 | }
55 | }
56 | )
57 | }
58 |
59 | return (
60 | <>
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | Let's create a new site
70 |
71 |
72 | Give your site a name, and you can start using Cusdis.
73 |
74 |
75 |
76 |
77 |
78 | Site name
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | >
89 | )
90 | }
91 |
92 | export async function getServerSideProps(ctx) {
93 |
94 | const session = await getSession(ctx.req)
95 |
96 | if (!session) {
97 | return {
98 | redirect: {
99 | destination: '/api/auth/signin',
100 | permanent: false
101 | }
102 | }
103 | }
104 |
105 | // const projectService = new ProjectService(ctx.req)
106 |
107 | // const defaultProject = await projectService.getFirstProject(session.uid, {
108 | // select: {
109 | // id: true
110 | // }
111 | // })
112 |
113 | // if (defaultProject) {
114 | // // redirect to project dashboard
115 | // return {
116 | // redirect: {
117 | // destination: `/dashboard/project/${defaultProject.id}`,
118 | // permanent: false
119 | // }
120 | // }
121 | // }
122 |
123 | return {
124 | props: {
125 |
126 | }
127 | }
128 | }
129 |
130 | export default GettingStart
--------------------------------------------------------------------------------
/pages/open/approve.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Stack, Title, Text, Divider, Textarea, Box, Button, Anchor } from "@mantine/core"
2 | import { notifications } from "@mantine/notifications"
3 | import { Comment, Page, Project } from "@prisma/client"
4 | import { useRouter } from "next/router"
5 | import React from "react"
6 | import { useMutation } from "react-query"
7 | import { Head } from "../../components/Head"
8 | import { CommentService } from "../../service/comment.service"
9 | import { SecretKey, TokenService } from "../../service/token.service"
10 | import { apiClient } from "../../utils.client"
11 | import { prisma } from "../../utils.server"
12 | import { ErrorCode } from "../error"
13 |
14 | const approveComment = async ({ token }) => {
15 | const res = await apiClient.post(`/open/approve?token=${token}`)
16 | return res.data
17 | }
18 |
19 | const appendReply = async ({ replyContent, token }) => {
20 | const res = await apiClient.post(`/open/approve?token=${token}`, {
21 | replyContent
22 | })
23 | return res.data
24 | }
25 |
26 | function ApprovePage(props: {
27 | comment: Comment & {
28 | page: Page & {
29 | project: Project
30 | }
31 | }
32 | }) {
33 |
34 | const router = useRouter()
35 |
36 | const [replyContent, setReplyContent] = React.useState('')
37 |
38 | const appendReplyMutation = useMutation(appendReply, {
39 | onSuccess() {
40 | notifications.show({
41 | title: 'Success',
42 | message: 'Reply appended',
43 | color: 'green'
44 | })
45 | setReplyContent('')
46 | },
47 | onError(data: any) {
48 | const {
49 | error: message,
50 | status: statusCode
51 | } = data.response.data
52 |
53 | notifications.show({
54 | title: "Error",
55 | message,
56 | color: 'yellow'
57 | })
58 | }
59 | })
60 | const approveCommentMutation = useMutation(approveComment, {
61 | onSuccess() {
62 | notifications.show({
63 | title: 'Success',
64 | message: 'Reply appended',
65 | color: 'green'
66 | })
67 |
68 | location.reload()
69 | },
70 | onError(data: any) {
71 | const {
72 | error: message,
73 | status: statusCode
74 | } = data.response.data
75 |
76 | notifications.show({
77 | title: "Error",
78 | message,
79 | color: 'yellow'
80 | })
81 | }
82 | })
83 |
84 | return (
85 | <>
86 |
87 |
88 |
89 |
90 | Cusdis
91 |
92 |
93 |
94 | New comment on site {props.comment.page.project.title}, page {props.comment.page.title || props.comment.page.slug}
95 | From: {props.comment.by_nickname} ({props.comment.by_email || 'Email not provided'})
96 | ({
97 | whiteSpace: 'pre-wrap',
98 | backgroundColor: theme.colors.gray[0],
99 | padding: theme.spacing.md
100 | })} component='pre' w="full" size="sm">{props.comment.content}
101 |
102 |
103 |
104 | {
105 | props.comment.approved ? :
112 | }
113 |
114 |
115 |
116 |
117 |
118 |
119 | * Appending reply to a comment will automatically approve the comment
120 |
121 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | >
134 | )
135 | }
136 |
137 | function redirectError(code: ErrorCode) {
138 | return {
139 | redirect: {
140 | destination: `/error?code=${code}`,
141 | permanent: false
142 | }
143 | }
144 | }
145 |
146 | export async function getServerSideProps(ctx) {
147 |
148 | const tokenService = new TokenService()
149 | const commentService = new CommentService(ctx.req)
150 |
151 | const { token } = ctx.query
152 |
153 | if (!token) {
154 | return redirectError(ErrorCode.INVALID_TOKEN)
155 | }
156 |
157 | let commentId
158 |
159 | try {
160 | commentId = tokenService.validate(token, SecretKey.ApproveComment).commentId
161 | } catch (e) {
162 | return redirectError(ErrorCode.INVALID_TOKEN)
163 | }
164 |
165 | const comment = await prisma.comment.findUnique({
166 | where: {
167 | id: commentId
168 | },
169 | select: {
170 | by_nickname: true,
171 | by_email: true,
172 | content: true,
173 | approved: true,
174 | page: {
175 | select: {
176 | title: true,
177 | slug: true,
178 | url: true,
179 | project: {
180 | select: {
181 | title: true
182 | }
183 | }
184 | }
185 | }
186 | }
187 | })
188 |
189 | return {
190 | props: {
191 | comment
192 | }
193 | }
194 | }
195 |
196 | export default ApprovePage
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {
4 | purge: ['widget/**/*.svelte', 'widget/theme.css'],
5 | darkMode: 'class',
6 | variants: {
7 | extend: {
8 | outline: ['dark'],
9 | borderWidth: ['dark'],
10 | borderColor: ['dark']
11 | },
12 | },
13 | },
14 | autoprefixer: {},
15 | },
16 | }
17 |
--------------------------------------------------------------------------------
/prisma/mysql/migrations/20211121064912_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE `accounts` (
3 | `id` INTEGER NOT NULL AUTO_INCREMENT,
4 | `compound_id` VARCHAR(191) NOT NULL,
5 | `user_id` VARCHAR(191) NOT NULL,
6 | `provider_type` VARCHAR(191) NOT NULL,
7 | `provider_id` VARCHAR(191) NOT NULL,
8 | `provider_account_id` VARCHAR(191) NOT NULL,
9 | `refresh_token` VARCHAR(191),
10 | `access_token` VARCHAR(191),
11 | `access_token_expires` DATETIME(3),
12 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
13 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
14 |
15 | UNIQUE INDEX `accounts.compound_id_unique`(`compound_id`),
16 | INDEX `providerAccountId`(`provider_account_id`),
17 | INDEX `providerId`(`provider_id`),
18 | INDEX `userId`(`user_id`),
19 | PRIMARY KEY (`id`)
20 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
21 |
22 | -- CreateTable
23 | CREATE TABLE `sessions` (
24 | `id` INTEGER NOT NULL AUTO_INCREMENT,
25 | `user_id` VARCHAR(191) NOT NULL,
26 | `expires` DATETIME(3) NOT NULL,
27 | `session_token` VARCHAR(191) NOT NULL,
28 | `access_token` VARCHAR(191) NOT NULL,
29 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
30 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
31 |
32 | UNIQUE INDEX `sessions.session_token_unique`(`session_token`),
33 | UNIQUE INDEX `sessions.access_token_unique`(`access_token`),
34 | PRIMARY KEY (`id`)
35 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
36 |
37 | -- CreateTable
38 | CREATE TABLE `users` (
39 | `id` VARCHAR(191) NOT NULL,
40 | `name` VARCHAR(191),
41 | `email` VARCHAR(191),
42 | `email_verified` DATETIME(3),
43 | `image` VARCHAR(191),
44 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
45 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
46 | `enable_new_comment_notification` BOOLEAN DEFAULT true,
47 | `notification_email` VARCHAR(191),
48 |
49 | UNIQUE INDEX `users.email_unique`(`email`),
50 | PRIMARY KEY (`id`)
51 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
52 |
53 | -- CreateTable
54 | CREATE TABLE `verification_requests` (
55 | `id` INTEGER NOT NULL AUTO_INCREMENT,
56 | `identifier` VARCHAR(191) NOT NULL,
57 | `token` VARCHAR(191) NOT NULL,
58 | `expires` DATETIME(3) NOT NULL,
59 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
60 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
61 |
62 | UNIQUE INDEX `verification_requests.token_unique`(`token`),
63 | PRIMARY KEY (`id`)
64 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
65 |
66 | -- CreateTable
67 | CREATE TABLE `projects` (
68 | `id` VARCHAR(191) NOT NULL,
69 | `title` VARCHAR(191) NOT NULL,
70 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
71 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
72 | `deleted_at` DATETIME(3),
73 | `ownerId` VARCHAR(191) NOT NULL,
74 | `token` VARCHAR(191),
75 | `fetch_latest_comments_at` DATETIME(3),
76 | `enable_notification` BOOLEAN DEFAULT true,
77 | `notification_email` VARCHAR(191),
78 | `webhook` VARCHAR(191),
79 | `enableWebhook` BOOLEAN,
80 |
81 | PRIMARY KEY (`id`)
82 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
83 |
84 | -- CreateTable
85 | CREATE TABLE `pages` (
86 | `id` VARCHAR(191) NOT NULL,
87 | `slug` VARCHAR(191) NOT NULL,
88 | `url` VARCHAR(191),
89 | `title` VARCHAR(191),
90 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
91 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
92 | `projectId` VARCHAR(191) NOT NULL,
93 |
94 | INDEX `projectId`(`projectId`),
95 | PRIMARY KEY (`id`)
96 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
97 |
98 | -- CreateTable
99 | CREATE TABLE `comments` (
100 | `id` VARCHAR(191) NOT NULL,
101 | `pageId` VARCHAR(191) NOT NULL,
102 | `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
103 | `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
104 | `deletedAt` DATETIME(3),
105 | `moderatorId` VARCHAR(191),
106 | `by_email` VARCHAR(191),
107 | `by_nickname` VARCHAR(191) NOT NULL,
108 | `content` TEXT NOT NULL,
109 | `approved` BOOLEAN NOT NULL DEFAULT false,
110 | `parentId` VARCHAR(191),
111 |
112 | PRIMARY KEY (`id`)
113 | ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
114 |
115 | -- AddForeignKey
116 | ALTER TABLE `projects` ADD FOREIGN KEY (`ownerId`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
117 |
118 | -- AddForeignKey
119 | ALTER TABLE `pages` ADD FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
120 |
121 | -- AddForeignKey
122 | ALTER TABLE `comments` ADD FOREIGN KEY (`pageId`) REFERENCES `pages`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
123 |
124 | -- AddForeignKey
125 | ALTER TABLE `comments` ADD FOREIGN KEY (`moderatorId`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
126 |
127 | -- AddForeignKey
128 | ALTER TABLE `comments` ADD FOREIGN KEY (`parentId`) REFERENCES `comments`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
129 |
--------------------------------------------------------------------------------
/prisma/mysql/migrations/20211124155649_add_display_name/migration.sql:
--------------------------------------------------------------------------------
1 | -- DropForeignKey
2 | ALTER TABLE `comments` DROP FOREIGN KEY `comments_ibfk_2`;
3 |
4 | -- DropForeignKey
5 | ALTER TABLE `comments` DROP FOREIGN KEY `comments_ibfk_1`;
6 |
7 | -- DropForeignKey
8 | ALTER TABLE `comments` DROP FOREIGN KEY `comments_ibfk_3`;
9 |
10 | -- DropForeignKey
11 | ALTER TABLE `pages` DROP FOREIGN KEY `pages_ibfk_1`;
12 |
13 | -- DropForeignKey
14 | ALTER TABLE `projects` DROP FOREIGN KEY `projects_ibfk_1`;
15 |
16 | -- AlterTable
17 | ALTER TABLE `users` ADD COLUMN `displayName` VARCHAR(191) NULL;
18 |
19 | -- AddForeignKey
20 | ALTER TABLE `projects` ADD CONSTRAINT `projects_ownerId_fkey` FOREIGN KEY (`ownerId`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
21 |
22 | -- AddForeignKey
23 | ALTER TABLE `pages` ADD CONSTRAINT `pages_projectId_fkey` FOREIGN KEY (`projectId`) REFERENCES `projects`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
24 |
25 | -- AddForeignKey
26 | ALTER TABLE `comments` ADD CONSTRAINT `comments_pageId_fkey` FOREIGN KEY (`pageId`) REFERENCES `pages`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
27 |
28 | -- AddForeignKey
29 | ALTER TABLE `comments` ADD CONSTRAINT `comments_moderatorId_fkey` FOREIGN KEY (`moderatorId`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
30 |
31 | -- AddForeignKey
32 | ALTER TABLE `comments` ADD CONSTRAINT `comments_parentId_fkey` FOREIGN KEY (`parentId`) REFERENCES `comments`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
33 |
34 | -- RenameIndex
35 | ALTER TABLE `accounts` RENAME INDEX `accounts.compound_id_unique` TO `accounts_compound_id_key`;
36 |
37 | -- RenameIndex
38 | ALTER TABLE `sessions` RENAME INDEX `sessions.access_token_unique` TO `sessions_access_token_key`;
39 |
40 | -- RenameIndex
41 | ALTER TABLE `sessions` RENAME INDEX `sessions.session_token_unique` TO `sessions_session_token_key`;
42 |
43 | -- RenameIndex
44 | ALTER TABLE `users` RENAME INDEX `users.email_unique` TO `users_email_key`;
45 |
46 | -- RenameIndex
47 | ALTER TABLE `verification_requests` RENAME INDEX `verification_requests.token_unique` TO `verification_requests_token_key`;
48 |
--------------------------------------------------------------------------------
/prisma/mysql/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "mysql"
--------------------------------------------------------------------------------
/prisma/mysql/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | datasource db {
5 | provider = "mysql"
6 | url = env("DB_URL")
7 | shadowDatabaseUrl = env("SHADOW_DB_URL")
8 | }
9 |
10 | generator client {
11 | provider = "prisma-client-js"
12 | previewFeatures = ["referentialActions"]
13 | }
14 |
15 | // next-auth BEGIN
16 |
17 | model Account {
18 | id Int @id @default(autoincrement())
19 | compoundId String @unique @map(name: "compound_id")
20 | userId String @map(name: "user_id")
21 | providerType String @map(name: "provider_type")
22 | providerId String @map(name: "provider_id")
23 | providerAccountId String @map(name: "provider_account_id")
24 | refreshToken String? @map(name: "refresh_token")
25 | accessToken String? @map(name: "access_token")
26 | accessTokenExpires DateTime? @map(name: "access_token_expires")
27 | createdAt DateTime @default(now()) @map(name: "created_at")
28 | updatedAt DateTime @default(now()) @map(name: "updated_at")
29 |
30 |
31 | @@index([providerAccountId], name: "providerAccountId")
32 | @@index([providerId], name: "providerId")
33 | @@index([userId], name: "userId")
34 | @@map(name: "accounts")
35 | }
36 |
37 | model Session {
38 | id Int @id @default(autoincrement())
39 | userId String @map(name: "user_id")
40 | expires DateTime
41 | sessionToken String @unique @map(name: "session_token")
42 | accessToken String @unique @map(name: "access_token")
43 | createdAt DateTime @default(now()) @map(name: "created_at")
44 | updatedAt DateTime @default(now()) @map(name: "updated_at")
45 |
46 | @@map(name: "sessions")
47 | }
48 |
49 | model User {
50 | id String @id @default(uuid())
51 | name String?
52 | displayName String?
53 | email String? @unique
54 | emailVerified DateTime? @map(name: "email_verified")
55 | image String?
56 | createdAt DateTime @default(now()) @map(name: "created_at")
57 | updatedAt DateTime @default(now()) @map(name: "updated_at")
58 |
59 | projects Project[]
60 |
61 | Comment Comment[]
62 |
63 | enableNewCommentNotification Boolean? @default(true) @map(name: "enable_new_comment_notification")
64 | notificationEmail String? @map(name: "notification_email")
65 |
66 | @@map(name: "users")
67 | }
68 |
69 | model VerificationRequest {
70 | id Int @id @default(autoincrement())
71 | identifier String
72 | token String @unique
73 | expires DateTime
74 | createdAt DateTime @default(now()) @map(name: "created_at")
75 | updatedAt DateTime @default(now()) @map(name: "updated_at")
76 |
77 | @@map(name: "verification_requests")
78 | }
79 |
80 | // next-auth END
81 |
82 | model Project {
83 | id String @id @default(uuid())
84 | title String
85 | createdAt DateTime @default(now()) @map(name: "created_at")
86 | updatedAt DateTime @default(now()) @map(name: "updated_at")
87 | deletedAt DateTime? @map(name: "deleted_at")
88 |
89 | ownerId String
90 | owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade)
91 |
92 | pages Page[]
93 |
94 | token String?
95 | fetchLatestCommentsAt DateTime? @map(name: "fetch_latest_comments_at")
96 |
97 | enableNotification Boolean? @default(true) @map(name: "enable_notification")
98 | notificationEmail String? @map(name: "notification_email")
99 |
100 | webhook String?
101 | enableWebhook Boolean?
102 |
103 | @@map(name: "projects")
104 | }
105 |
106 | model Page {
107 | id String @id @default(uuid())
108 |
109 | slug String
110 | url String?
111 | title String?
112 |
113 | createdAt DateTime @default(now()) @map(name: "created_at")
114 | updatedAt DateTime @default(now()) @map(name: "updated_at")
115 |
116 | projectId String
117 | project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
118 |
119 | comments Comment[]
120 |
121 |
122 | @@index([projectId], name: "projectId")
123 | @@map("pages")
124 | }
125 |
126 | model Comment {
127 | id String @id @default(uuid())
128 |
129 | pageId String
130 | page Page @relation(fields: [pageId], references: [id], onDelete: Cascade)
131 |
132 | createdAt DateTime @default(now()) @map(name: "created_at")
133 | updatedAt DateTime @default(now()) @map(name: "updated_at")
134 | deletedAt DateTime?
135 |
136 | moderatorId String?
137 | moderator User? @relation(fields: [moderatorId], references: [id])
138 | by_email String?
139 | by_nickname String
140 | content String @db.Text
141 |
142 | approved Boolean @default(false)
143 |
144 | parentId String?
145 | parent Comment? @relation("replies", fields: [parentId], references: [id])
146 | replies Comment[] @relation("replies")
147 |
148 | @@map("comments")
149 | }
150 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20210417102821_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "accounts" (
3 | "id" SERIAL NOT NULL,
4 | "compound_id" TEXT NOT NULL,
5 | "user_id" TEXT NOT NULL,
6 | "provider_type" TEXT NOT NULL,
7 | "provider_id" TEXT NOT NULL,
8 | "provider_account_id" TEXT NOT NULL,
9 | "refresh_token" TEXT,
10 | "access_token" TEXT,
11 | "access_token_expires" TIMESTAMP(3),
12 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
13 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
14 |
15 | PRIMARY KEY ("id")
16 | );
17 |
18 | -- CreateTable
19 | CREATE TABLE "sessions" (
20 | "id" SERIAL NOT NULL,
21 | "user_id" TEXT NOT NULL,
22 | "expires" TIMESTAMP(3) NOT NULL,
23 | "session_token" TEXT NOT NULL,
24 | "access_token" TEXT NOT NULL,
25 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
26 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
27 |
28 | PRIMARY KEY ("id")
29 | );
30 |
31 | -- CreateTable
32 | CREATE TABLE "users" (
33 | "id" TEXT NOT NULL,
34 | "name" TEXT,
35 | "email" TEXT,
36 | "email_verified" TIMESTAMP(3),
37 | "image" TEXT,
38 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
39 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
40 |
41 | PRIMARY KEY ("id")
42 | );
43 |
44 | -- CreateTable
45 | CREATE TABLE "verification_requests" (
46 | "id" SERIAL NOT NULL,
47 | "identifier" TEXT NOT NULL,
48 | "token" TEXT NOT NULL,
49 | "expires" TIMESTAMP(3) NOT NULL,
50 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
51 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
52 |
53 | PRIMARY KEY ("id")
54 | );
55 |
56 | -- CreateTable
57 | CREATE TABLE "projects" (
58 | "id" TEXT NOT NULL,
59 | "title" TEXT NOT NULL,
60 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
61 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
62 | "ownerId" TEXT NOT NULL,
63 |
64 | PRIMARY KEY ("id")
65 | );
66 |
67 | -- CreateTable
68 | CREATE TABLE "pages" (
69 | "id" TEXT NOT NULL,
70 | "slug" TEXT NOT NULL,
71 | "url" TEXT,
72 | "title" TEXT,
73 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
74 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
75 | "projectId" TEXT NOT NULL,
76 |
77 | PRIMARY KEY ("id")
78 | );
79 |
80 | -- CreateTable
81 | CREATE TABLE "comments" (
82 | "id" TEXT NOT NULL,
83 | "pageId" TEXT NOT NULL,
84 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
85 | "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
86 | "deletedAt" TIMESTAMP(3),
87 | "moderatorId" TEXT,
88 | "by_email" TEXT,
89 | "by_nickname" TEXT NOT NULL,
90 | "content" TEXT NOT NULL,
91 | "approved" BOOLEAN NOT NULL DEFAULT false,
92 | "parentId" TEXT,
93 |
94 | PRIMARY KEY ("id")
95 | );
96 |
97 | -- CreateIndex
98 | CREATE UNIQUE INDEX "accounts.compound_id_unique" ON "accounts"("compound_id");
99 |
100 | -- CreateIndex
101 | CREATE INDEX "providerAccountId" ON "accounts"("provider_account_id");
102 |
103 | -- CreateIndex
104 | CREATE INDEX "providerId" ON "accounts"("provider_id");
105 |
106 | -- CreateIndex
107 | CREATE INDEX "userId" ON "accounts"("user_id");
108 |
109 | -- CreateIndex
110 | CREATE UNIQUE INDEX "sessions.session_token_unique" ON "sessions"("session_token");
111 |
112 | -- CreateIndex
113 | CREATE UNIQUE INDEX "sessions.access_token_unique" ON "sessions"("access_token");
114 |
115 | -- CreateIndex
116 | CREATE UNIQUE INDEX "users.email_unique" ON "users"("email");
117 |
118 | -- CreateIndex
119 | CREATE UNIQUE INDEX "verification_requests.token_unique" ON "verification_requests"("token");
120 |
121 | -- CreateIndex
122 | CREATE INDEX "projectId" ON "pages"("projectId");
123 |
124 | -- AddForeignKey
125 | ALTER TABLE "projects" ADD FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
126 |
127 | -- AddForeignKey
128 | ALTER TABLE "pages" ADD FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
129 |
130 | -- AddForeignKey
131 | ALTER TABLE "comments" ADD FOREIGN KEY ("pageId") REFERENCES "pages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
132 |
133 | -- AddForeignKey
134 | ALTER TABLE "comments" ADD FOREIGN KEY ("moderatorId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
135 |
136 | -- AddForeignKey
137 | ALTER TABLE "comments" ADD FOREIGN KEY ("parentId") REFERENCES "comments"("id") ON DELETE SET NULL ON UPDATE CASCADE;
138 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20210422105059_add_project_token/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "token" TEXT,
3 | ADD COLUMN "fetch_latest_comments_at" TIMESTAMP(3);
4 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20210423071344_notification/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "enable_notification" BOOLEAN DEFAULT true;
3 |
4 | -- AlterTable
5 | ALTER TABLE "users" ADD COLUMN "enable_new_comment_notification" BOOLEAN DEFAULT true,
6 | ADD COLUMN "notification_email" TEXT;
7 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20210426145530_project_webhook/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "webhook" TEXT,
3 | ADD COLUMN "enableWebhook" BOOLEAN;
4 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20210427123424_project_deleted_at/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "deleted_at" TIMESTAMP(3);
3 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20211124155429_add_display_name/migration.sql:
--------------------------------------------------------------------------------
1 | -- DropForeignKey
2 | ALTER TABLE "comments" DROP CONSTRAINT "comments_pageId_fkey";
3 |
4 | -- DropForeignKey
5 | ALTER TABLE "pages" DROP CONSTRAINT "pages_projectId_fkey";
6 |
7 | -- DropForeignKey
8 | ALTER TABLE "projects" DROP CONSTRAINT "projects_ownerId_fkey";
9 |
10 | -- AlterTable
11 | ALTER TABLE "users" ADD COLUMN "displayName" TEXT;
12 |
13 | -- AddForeignKey
14 | ALTER TABLE "projects" ADD CONSTRAINT "projects_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
15 |
16 | -- AddForeignKey
17 | ALTER TABLE "pages" ADD CONSTRAINT "pages_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
18 |
19 | -- AddForeignKey
20 | ALTER TABLE "comments" ADD CONSTRAINT "comments_pageId_fkey" FOREIGN KEY ("pageId") REFERENCES "pages"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
21 |
22 | -- RenameIndex
23 | ALTER INDEX "accounts.compound_id_unique" RENAME TO "accounts_compound_id_key";
24 |
25 | -- RenameIndex
26 | ALTER INDEX "sessions.access_token_unique" RENAME TO "sessions_access_token_key";
27 |
28 | -- RenameIndex
29 | ALTER INDEX "sessions.session_token_unique" RENAME TO "sessions_session_token_key";
30 |
31 | -- RenameIndex
32 | ALTER INDEX "users.email_unique" RENAME TO "users_email_key";
33 |
34 | -- RenameIndex
35 | ALTER INDEX "verification_requests.token_unique" RENAME TO "verification_requests_token_key";
36 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/20230713082802_subscription/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Subscription" (
3 | "id" TEXT NOT NULL,
4 | "userId" TEXT NOT NULL,
5 | "lemon_subscription_id" TEXT,
6 | "order_id" TEXT NOT NULL,
7 | "product_id" TEXT,
8 | "variant_id" TEXT,
9 | "customer_id" TEXT,
10 | "status" TEXT NOT NULL,
11 | "endsAt" TIMESTAMP(3),
12 | "update_payment_method_url" TEXT,
13 | "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
14 |
15 | CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
16 | );
17 |
18 | -- CreateTable
19 | CREATE TABLE "usages" (
20 | "id" TEXT NOT NULL,
21 | "userId" TEXT NOT NULL,
22 | "label" TEXT NOT NULL,
23 | "count" INTEGER NOT NULL DEFAULT 0,
24 | "updatedAt" TIMESTAMP(3) NOT NULL,
25 |
26 | CONSTRAINT "usages_pkey" PRIMARY KEY ("id")
27 | );
28 |
29 | -- CreateIndex
30 | CREATE UNIQUE INDEX "Subscription_userId_key" ON "Subscription"("userId");
31 |
32 | -- CreateIndex
33 | CREATE UNIQUE INDEX "usages_userId_label_key" ON "usages"("userId", "label");
34 |
35 | -- AddForeignKey
36 | ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
37 |
38 | -- AddForeignKey
39 | ALTER TABLE "usages" ADD CONSTRAINT "usages_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
40 |
--------------------------------------------------------------------------------
/prisma/pgsql/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "postgresql"
--------------------------------------------------------------------------------
/prisma/pgsql/schema.prisma:
--------------------------------------------------------------------------------
1 | // This is your Prisma schema file,
2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema
3 |
4 | datasource db {
5 | provider = "postgresql"
6 | url = env("DB_URL")
7 | }
8 |
9 | generator client {
10 | provider = "prisma-client-js"
11 | }
12 |
13 | // next-auth BEGIN
14 |
15 | model Account {
16 | id Int @id @default(autoincrement())
17 | compoundId String @unique @map(name: "compound_id")
18 | userId String @map(name: "user_id")
19 | providerType String @map(name: "provider_type")
20 | providerId String @map(name: "provider_id")
21 | providerAccountId String @map(name: "provider_account_id")
22 | refreshToken String? @map(name: "refresh_token")
23 | accessToken String? @map(name: "access_token")
24 | accessTokenExpires DateTime? @map(name: "access_token_expires")
25 | createdAt DateTime @default(now()) @map(name: "created_at")
26 | updatedAt DateTime @default(now()) @map(name: "updated_at")
27 |
28 | @@index([providerAccountId], name: "providerAccountId")
29 | @@index([providerId], name: "providerId")
30 | @@index([userId], name: "userId")
31 | @@map(name: "accounts")
32 | }
33 |
34 | model Session {
35 | id Int @id @default(autoincrement())
36 | userId String @map(name: "user_id")
37 | expires DateTime
38 | sessionToken String @unique @map(name: "session_token")
39 | accessToken String @unique @map(name: "access_token")
40 | createdAt DateTime @default(now()) @map(name: "created_at")
41 | updatedAt DateTime @default(now()) @map(name: "updated_at")
42 |
43 | @@map(name: "sessions")
44 | }
45 |
46 | model User {
47 | id String @id @default(uuid())
48 | name String?
49 | displayName String?
50 | email String? @unique
51 | emailVerified DateTime? @map(name: "email_verified")
52 | image String?
53 | createdAt DateTime @default(now()) @map(name: "created_at")
54 | updatedAt DateTime @default(now()) @map(name: "updated_at")
55 |
56 | projects Project[]
57 |
58 | Comment Comment[]
59 |
60 | subscription Subscription?
61 |
62 | enableNewCommentNotification Boolean? @default(true) @map(name: "enable_new_comment_notification")
63 | notificationEmail String? @map(name: "notification_email")
64 |
65 | usage Usage[]
66 |
67 | @@map(name: "users")
68 | }
69 |
70 | model Subscription {
71 | id String @id @default(uuid())
72 |
73 | userId String @unique
74 | user User @relation(fields: [userId], references: [id])
75 |
76 | lemonSubscriptionId String? @map(name: "lemon_subscription_id")
77 | orderId String @map(name: "order_id")
78 | productId String? @map(name: "product_id")
79 | variantId String? @map(name: "variant_id")
80 | customerId String? @map(name: "customer_id")
81 | status String
82 | endsAt DateTime? @map(name: "endsAt")
83 |
84 | updatePaymentMethodUrl String? @map(name: "update_payment_method_url")
85 |
86 | createdAt DateTime @default(now()) @map(name: "created_at")
87 | }
88 |
89 | model VerificationRequest {
90 | id Int @id @default(autoincrement())
91 | identifier String
92 | token String @unique
93 | expires DateTime
94 | createdAt DateTime @default(now()) @map(name: "created_at")
95 | updatedAt DateTime @default(now()) @map(name: "updated_at")
96 |
97 | @@map(name: "verification_requests")
98 | }
99 |
100 | // next-auth END
101 |
102 | model Project {
103 | id String @id @default(uuid())
104 | title String
105 | createdAt DateTime @default(now()) @map(name: "created_at")
106 | updatedAt DateTime @default(now()) @map(name: "updated_at")
107 | deletedAt DateTime? @map(name: "deleted_at")
108 |
109 | ownerId String
110 | owner User @relation(fields: [ownerId], references: [id])
111 |
112 | pages Page[]
113 |
114 | token String?
115 | fetchLatestCommentsAt DateTime? @map(name: "fetch_latest_comments_at")
116 |
117 | enableNotification Boolean? @default(true) @map(name: "enable_notification")
118 |
119 | webhook String?
120 | enableWebhook Boolean?
121 |
122 | @@map(name: "projects")
123 | }
124 |
125 | model Page {
126 | id String @id @default(uuid())
127 |
128 | slug String
129 | url String?
130 | title String?
131 |
132 | createdAt DateTime @default(now()) @map(name: "created_at")
133 | updatedAt DateTime @default(now()) @map(name: "updated_at")
134 |
135 | projectId String
136 | project Project @relation(fields: [projectId], references: [id])
137 |
138 | comments Comment[]
139 |
140 | @@index([projectId], name: "projectId")
141 | @@map("pages")
142 | }
143 |
144 | model Comment {
145 | id String @id @default(uuid())
146 |
147 | pageId String
148 | page Page @relation(fields: [pageId], references: [id])
149 |
150 | createdAt DateTime @default(now()) @map(name: "created_at")
151 | updatedAt DateTime @default(now()) @map(name: "updated_at")
152 | deletedAt DateTime?
153 |
154 | moderatorId String?
155 | moderator User? @relation(fields: [moderatorId], references: [id])
156 | by_email String?
157 | by_nickname String
158 | content String
159 |
160 | approved Boolean @default(false)
161 |
162 | parentId String?
163 | parent Comment? @relation("replies", fields: [parentId], references: [id])
164 | replies Comment[] @relation("replies")
165 |
166 | @@map("comments")
167 | }
168 |
169 | model Usage {
170 | id String @id @default(uuid())
171 |
172 | userId String
173 | user User @relation(fields: [userId], references: [id])
174 |
175 | label String
176 | count Int @default(0)
177 |
178 | updatedAt DateTime @updatedAt
179 |
180 | @@unique([userId, label])
181 |
182 | @@map("usages")
183 | }
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210419153654_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "accounts" (
3 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4 | "compound_id" TEXT NOT NULL,
5 | "user_id" TEXT NOT NULL,
6 | "provider_type" TEXT NOT NULL,
7 | "provider_id" TEXT NOT NULL,
8 | "provider_account_id" TEXT NOT NULL,
9 | "refresh_token" TEXT,
10 | "access_token" TEXT,
11 | "access_token_expires" DATETIME,
12 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
13 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
14 | );
15 |
16 | -- CreateTable
17 | CREATE TABLE "sessions" (
18 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
19 | "user_id" TEXT NOT NULL,
20 | "expires" DATETIME NOT NULL,
21 | "session_token" TEXT NOT NULL,
22 | "access_token" TEXT NOT NULL,
23 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
24 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
25 | );
26 |
27 | -- CreateTable
28 | CREATE TABLE "users" (
29 | "id" TEXT NOT NULL PRIMARY KEY,
30 | "name" TEXT,
31 | "email" TEXT,
32 | "email_verified" DATETIME,
33 | "image" TEXT,
34 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
35 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
36 | );
37 |
38 | -- CreateTable
39 | CREATE TABLE "verification_requests" (
40 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
41 | "identifier" TEXT NOT NULL,
42 | "token" TEXT NOT NULL,
43 | "expires" DATETIME NOT NULL,
44 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
45 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
46 | );
47 |
48 | -- CreateTable
49 | CREATE TABLE "projects" (
50 | "id" TEXT NOT NULL PRIMARY KEY,
51 | "title" TEXT NOT NULL,
52 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
53 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
54 | "ownerId" TEXT NOT NULL,
55 | FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
56 | );
57 |
58 | -- CreateTable
59 | CREATE TABLE "pages" (
60 | "id" TEXT NOT NULL PRIMARY KEY,
61 | "slug" TEXT NOT NULL,
62 | "url" TEXT,
63 | "title" TEXT,
64 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
65 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
66 | "projectId" TEXT NOT NULL,
67 | FOREIGN KEY ("projectId") REFERENCES "projects" ("id") ON DELETE CASCADE ON UPDATE CASCADE
68 | );
69 |
70 | -- CreateTable
71 | CREATE TABLE "comments" (
72 | "id" TEXT NOT NULL PRIMARY KEY,
73 | "pageId" TEXT NOT NULL,
74 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
75 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
76 | "deletedAt" DATETIME,
77 | "moderatorId" TEXT,
78 | "by_email" TEXT,
79 | "by_nickname" TEXT NOT NULL,
80 | "content" TEXT NOT NULL,
81 | "approved" BOOLEAN NOT NULL DEFAULT false,
82 | "parentId" TEXT,
83 | FOREIGN KEY ("pageId") REFERENCES "pages" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
84 | FOREIGN KEY ("moderatorId") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
85 | FOREIGN KEY ("parentId") REFERENCES "comments" ("id") ON DELETE SET NULL ON UPDATE CASCADE
86 | );
87 |
88 | -- CreateIndex
89 | CREATE UNIQUE INDEX "accounts.compound_id_unique" ON "accounts"("compound_id");
90 |
91 | -- CreateIndex
92 | CREATE INDEX "providerAccountId" ON "accounts"("provider_account_id");
93 |
94 | -- CreateIndex
95 | CREATE INDEX "providerId" ON "accounts"("provider_id");
96 |
97 | -- CreateIndex
98 | CREATE INDEX "userId" ON "accounts"("user_id");
99 |
100 | -- CreateIndex
101 | CREATE UNIQUE INDEX "sessions.session_token_unique" ON "sessions"("session_token");
102 |
103 | -- CreateIndex
104 | CREATE UNIQUE INDEX "sessions.access_token_unique" ON "sessions"("access_token");
105 |
106 | -- CreateIndex
107 | CREATE UNIQUE INDEX "users.email_unique" ON "users"("email");
108 |
109 | -- CreateIndex
110 | CREATE UNIQUE INDEX "verification_requests.token_unique" ON "verification_requests"("token");
111 |
112 | -- CreateIndex
113 | CREATE INDEX "projectId" ON "pages"("projectId");
114 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210422061617_add_project_token/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "token" TEXT;
3 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210422101358_add_fetch_latest_comments_at/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "fetch_latest_comments_at" DATETIME;
3 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210422171715_notification_info/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "users" ADD COLUMN "enable_new_comment_notification" BOOLEAN DEFAULT true;
3 | ALTER TABLE "users" ADD COLUMN "notification_email" TEXT;
4 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210423063317_project_notification_settings/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "enable_notification" BOOLEAN DEFAULT true;
3 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210426135406_add_project_webhook/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "webhook" TEXT;
3 | ALTER TABLE "projects" ADD COLUMN "enableWebhook" BOOLEAN;
4 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20210427114259_add_project_deleted_at/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "projects" ADD COLUMN "deleted_at" DATETIME;
3 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20211124142411_add_display_name/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "users" ADD COLUMN "displayName" TEXT;
3 |
4 | -- RedefineTables
5 | PRAGMA foreign_keys=OFF;
6 | CREATE TABLE "new_pages" (
7 | "id" TEXT NOT NULL PRIMARY KEY,
8 | "slug" TEXT NOT NULL,
9 | "url" TEXT,
10 | "title" TEXT,
11 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
12 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
13 | "projectId" TEXT NOT NULL,
14 | CONSTRAINT "pages_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
15 | );
16 | INSERT INTO "new_pages" ("created_at", "id", "projectId", "slug", "title", "updated_at", "url") SELECT "created_at", "id", "projectId", "slug", "title", "updated_at", "url" FROM "pages";
17 | DROP TABLE "pages";
18 | ALTER TABLE "new_pages" RENAME TO "pages";
19 | CREATE INDEX "projectId" ON "pages"("projectId");
20 | CREATE TABLE "new_projects" (
21 | "id" TEXT NOT NULL PRIMARY KEY,
22 | "title" TEXT NOT NULL,
23 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
24 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
25 | "deleted_at" DATETIME,
26 | "ownerId" TEXT NOT NULL,
27 | "token" TEXT,
28 | "fetch_latest_comments_at" DATETIME,
29 | "enable_notification" BOOLEAN DEFAULT true,
30 | "webhook" TEXT,
31 | "enableWebhook" BOOLEAN,
32 | CONSTRAINT "projects_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
33 | );
34 | INSERT INTO "new_projects" ("created_at", "deleted_at", "enableWebhook", "enable_notification", "fetch_latest_comments_at", "id", "ownerId", "title", "token", "updated_at", "webhook") SELECT "created_at", "deleted_at", "enableWebhook", "enable_notification", "fetch_latest_comments_at", "id", "ownerId", "title", "token", "updated_at", "webhook" FROM "projects";
35 | DROP TABLE "projects";
36 | ALTER TABLE "new_projects" RENAME TO "projects";
37 | CREATE TABLE "new_comments" (
38 | "id" TEXT NOT NULL PRIMARY KEY,
39 | "pageId" TEXT NOT NULL,
40 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
41 | "updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
42 | "deletedAt" DATETIME,
43 | "moderatorId" TEXT,
44 | "by_email" TEXT,
45 | "by_nickname" TEXT NOT NULL,
46 | "content" TEXT NOT NULL,
47 | "approved" BOOLEAN NOT NULL DEFAULT false,
48 | "parentId" TEXT,
49 | CONSTRAINT "comments_pageId_fkey" FOREIGN KEY ("pageId") REFERENCES "pages" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
50 | CONSTRAINT "comments_moderatorId_fkey" FOREIGN KEY ("moderatorId") REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
51 | CONSTRAINT "comments_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "comments" ("id") ON DELETE SET NULL ON UPDATE CASCADE
52 | );
53 | INSERT INTO "new_comments" ("approved", "by_email", "by_nickname", "content", "created_at", "deletedAt", "id", "moderatorId", "pageId", "parentId", "updated_at") SELECT "approved", "by_email", "by_nickname", "content", "created_at", "deletedAt", "id", "moderatorId", "pageId", "parentId", "updated_at" FROM "comments";
54 | DROP TABLE "comments";
55 | ALTER TABLE "new_comments" RENAME TO "comments";
56 | PRAGMA foreign_key_check;
57 | PRAGMA foreign_keys=ON;
58 |
59 | -- RedefineIndex
60 | DROP INDEX "accounts.compound_id_unique";
61 | CREATE UNIQUE INDEX "accounts_compound_id_key" ON "accounts"("compound_id");
62 |
63 | -- RedefineIndex
64 | DROP INDEX "sessions.access_token_unique";
65 | CREATE UNIQUE INDEX "sessions_access_token_key" ON "sessions"("access_token");
66 |
67 | -- RedefineIndex
68 | DROP INDEX "sessions.session_token_unique";
69 | CREATE UNIQUE INDEX "sessions_session_token_key" ON "sessions"("session_token");
70 |
71 | -- RedefineIndex
72 | DROP INDEX "users.email_unique";
73 | CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
74 |
75 | -- RedefineIndex
76 | DROP INDEX "verification_requests.token_unique";
77 | CREATE UNIQUE INDEX "verification_requests_token_key" ON "verification_requests"("token");
78 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20230713042856_subscription/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "Subscription" (
3 | "id" TEXT NOT NULL PRIMARY KEY,
4 | "userId" TEXT NOT NULL,
5 | "order_id" TEXT NOT NULL,
6 | "product_id" TEXT,
7 | "variant_id" TEXT,
8 | "customer_id" TEXT,
9 | "status" TEXT NOT NULL,
10 | "endsAt" DATETIME,
11 | "update_payment_method_url" TEXT,
12 | "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
13 | CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
14 | );
15 |
16 | -- CreateIndex
17 | CREATE UNIQUE INDEX "Subscription_userId_key" ON "Subscription"("userId");
18 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20230713055831_subscription/migration.sql:
--------------------------------------------------------------------------------
1 | -- AlterTable
2 | ALTER TABLE "Subscription" ADD COLUMN "lemon_subscription_id" TEXT;
3 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/20230713070441_usage/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "usages" (
3 | "id" TEXT NOT NULL PRIMARY KEY,
4 | "userId" TEXT NOT NULL,
5 | "label" TEXT NOT NULL,
6 | "count" INTEGER NOT NULL DEFAULT 0,
7 | "updatedAt" DATETIME NOT NULL,
8 | CONSTRAINT "usages_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
9 | );
10 |
11 | -- CreateIndex
12 | CREATE UNIQUE INDEX "usages_userId_label_key" ON "usages"("userId", "label");
13 |
--------------------------------------------------------------------------------
/prisma/sqlite/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/public/discussion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/discussion.png
--------------------------------------------------------------------------------
/public/doc/README.md:
--------------------------------------------------------------------------------
1 | # Cusdis
2 |
3 | Open-source, lightweight (~5kb gzipped), privacy-friendly alternative to Disqus.
4 |
5 |
6 | ## Features
7 |
8 | - Universal embed code
9 | - You can embed Cusdis on every website.
10 | - Light-weight sdk
11 | - The SDK that embed to your website is only 5kb (gzipped). Compared to Disqus (which is 24kb gzipped), it's very light-weight.
12 | - Email notification
13 | - One-click import data from Disqus
14 | - Moderation dashboard
15 | - Since we don't require user sign in to comment, all comments are NOT displayed by default, until the moderator approve it. We provide a moderation dashboard for it.
16 |
17 | There are two ways to use Cusdis:
18 |
19 | ## Self host
20 |
21 | _Pros: You own your data_
22 |
23 | You can install Cusdis on your own server, just follow this [installation guide](/self-host/vercel.md)
24 |
25 | ## Hosted service
26 |
27 | _Pros: Easy to use_
28 |
29 | You can also use our [hosted service](https://cusdis.com/dashboard). We host our service on [Vercel](https://vercel.com), the data is stored on a PostgreSQL database.
30 |
31 | ## Compared to Disqus
32 |
33 | Cusdis is not designed for a FULLY alternative to Disqus, it's aim to implement a minimist embed comment tool for small sites (like your static blog).
34 |
35 | Below are the pros and cons of Cusdis:
36 |
37 | ### Pros
38 |
39 | - Cusdis is open-source and self-hostable, you own your data.
40 | - The SDK is lightweight (~5kb gzipped)
41 | - Cusdis doesn't required commenter to sign in. We don't use cookies at all.
42 |
43 | ### Cons
44 |
45 | - Cusdis is on early development stage
46 | - You have to manually moderate comments which are not display by default until you approve it, since we dont't have a spam filter.
47 | - Disqus is a company, we aren't.
48 |
--------------------------------------------------------------------------------
/public/doc/_sidebar.md:
--------------------------------------------------------------------------------
1 | - **Introduction**
2 | - [About](/)
3 | - [Awesome Cusdis](awesome.md)
4 | - **Self-host Guide**
5 | - [Docker](/self-host/docker.md)
6 | - [Railway](/self-host/railway.md)
7 | - [Vercel](/self-host/vercel.md)
8 | - [Manually Install](/self-host/manual.md)
9 | - **Integration**
10 | - [JS SDK Reference](/advanced/sdk.md)
11 | - [React](https://github.com/Cusdis/sdk/tree/master/packages/react-cusdis)
12 | - [Vue](https://github.com/2nthony/vue-cusdis)
13 | - [Docsify](/integration/docsify.md)
14 | - [Jekyll](/integration/jekyll.md)
15 | - [Hugo](https://discourse.gohugo.io/t/free-and-open-source-comments-for-hugo/32940)
16 | - [Hexo](https://blog.cusdis.com/integate-cusdis-in-hexo)
17 | - [Mkdocs](/integration/mkdocs.md)
18 | - [Publii](/integration/publii.md)
19 | - **Features**
20 | - [Moderation](/features/moderation.md)
21 | - [Email Notification](/features/notification.md)
22 | - **Advanced**
23 | - [Webhook](/advanced/webhook.md)
24 | - [Show comment count](/advanced/show-comment-count.md)
25 | - [i18n](/advanced/i18n.md)
26 | - [**Contributing Guide**](/contributing.md)
27 | - [**FAQ**](/faq.md)
28 |
--------------------------------------------------------------------------------
/public/doc/advanced/i18n.md:
--------------------------------------------------------------------------------
1 | # i18n
2 |
3 | Cusdis comment widget has international support. But for keeping the SDK lightweight, only English is included.
4 |
5 | ## Usage
6 |
7 | Before the sdk script, add locale specific script: `https://cusdis.com/js/widget/lang/{LANG_CODE}.js`, for example:
8 |
9 | ```diff
10 |
17 | +
18 |
19 | ```
20 |
21 | > Make sure the locale specific script is loaded before the sdk.
22 |
23 | > Should change the script host to your own server if you are using self-host Cusdis. (e.g. `https://your-domain.com/js/widget/lang/zh-cn.js` )
24 |
25 | ## Current support language
26 |
27 | - zh-cn
28 | - zh-tw
29 | - ja
30 | - es
31 | - tr
32 | - pt-BR
33 | - oc
34 | - fr
35 | - id
36 | - ca
37 | - fi
38 | - ar
39 |
40 | ## Contributing more languages
41 |
42 | You are very welcome to contribute your language! Just create a file in `widget/lang/{LANG_CODE}.js` with:
43 |
44 | ```js
45 | window.CUSDIS_LOCALE = {
46 | //...
47 | }
48 | ```
49 |
50 | You can find all available keys in https://github.com/djyde/cusdis/blob/master/widget/lang/en.js.
51 |
52 | Feel free to create a PR!
53 |
--------------------------------------------------------------------------------
/public/doc/advanced/sdk.md:
--------------------------------------------------------------------------------
1 | # JS SDK
2 |
3 | Understand how the JS SDK works help you integrate Cusdis to an existed system.
4 |
5 | To embed the comment widget to your web page, you need to put **the element and JS SDK** on the page, at the position where you want to embed to:
6 |
7 | ```html
8 |
15 |
16 | ```
17 |
18 | > If you are using self-hosted Cusdis, remember changing the `data-host` and the host in `
12 |
13 | ```
14 |
15 | This script will collect all `data-cusdis-count-page-id` in current page and fetch the comments count. Then replace the count number to the element.
16 |
17 | !> Don't forget to change `https://cusdis.com` to your own domain if you are using self-host version.
18 |
19 | ?> If there are more than one element with `data-cusdis-count-page-id`, the script will batch the query in one.
20 |
21 | ## API
22 |
23 | This UMD script expose `CUSDIS_COUNT` on `window` object.
24 |
25 | ### window.CUSDIS_COUNT.initial()
26 |
27 | Manually update the count in the page.
28 |
--------------------------------------------------------------------------------
/public/doc/advanced/webhook.md:
--------------------------------------------------------------------------------
1 | # Webhook
2 |
3 | In addition to get new comment notification from Email, we also provide Webhook.
4 |
5 | ## Enable
6 |
7 | To enable webhook for project, in `Project` -> `Settings`, save your webhook url and turn on the switch button.
8 |
9 | 
10 |
11 | ## Reference
12 |
13 | ### New comment
14 |
15 | When new comment comes in, Cusdis will make a `POST` request to your webhook, with below data:
16 |
17 | ```js
18 | {
19 | "type": "new_comment",
20 | "data": {
21 | "by_nickname": "xxx",
22 | "by_email": "xxx",
23 | "content": "xxx",
24 | "page_id": "xxx",
25 | "page_title": "xxx", // page title, maybe NULL
26 | "project_title": "haha", // project title
27 | "approve_link": "" // use this link to approve this comment without login
28 | }
29 | }
30 | ```
31 |
32 | ## Official Telegram bot
33 |
34 | We also provide an official Telegram bot to send notification to you, with the power of Webhook:
35 |
36 | 1. Open and start bot https://t.me/CusdisBot
37 | 2. send `/gethook` command
38 | 3. Copy the URL result and paste in Cusdis project's webhook settings
39 |
--------------------------------------------------------------------------------
/public/doc/contributing.md:
--------------------------------------------------------------------------------
1 | # Contributing to Cusdis
2 |
3 | Thanks for taking time to contribute!
4 |
5 | This guide help you know everything about how to run Cusdis on your local machine and start developing.
6 |
7 | ## Start dev server
8 |
9 | Firstly, create a `.env` file:
10 |
11 | ```shell
12 | DB_URL=file:./db.sqlite
13 | USERNAME=admin
14 | PASSWORD=password
15 | JWT_SECRET=ofcourseistillloveyou
16 | ```
17 |
18 | ```bash
19 | # install dependencies
20 | $ yarn
21 |
22 | # start dev server
23 | $ yarn dev
24 | ```
25 |
26 | Now open http://localhost:3000 and signin with `admin` and `password`
27 |
28 | ### Using PostgreSQL
29 |
30 | `yarn dev` is using SQLite by default. If you want to develop with PostgreSQL, first change `DB_URL` in `.env` to your db connection url:
31 |
32 | ```shell
33 | # .env
34 | DB_URL=postgres://xxx
35 | ...
36 | ```
37 |
38 | Then use `yarn dev:pg` to start the dev server.
39 |
40 | ## Developing widget
41 |
42 | ```bash
43 | $ yarn widget
44 | ```
45 |
46 | The widget demo will run on http://localhost:3001
47 |
48 | Change the attributes of the widget in `widget/index.html` (Don't commit this file if you only modify something for testing).
49 |
50 | ## Modify schema
51 |
52 | Database schema is defined in `prisma/$DB_TYPE/schema.prisma`.
53 |
54 | ### Generate database migrations
55 |
56 | In general, you don't need to generate migration when contribute a new feature. Create a PR and the core team member will do this for you.
57 |
58 |
--------------------------------------------------------------------------------
/public/doc/faq.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | ## What if I delete a project?
4 |
5 | 1. You won't see the project in dashboard
6 | 2. Comment widget using this project's id won't be displayed in your website
--------------------------------------------------------------------------------
/public/doc/features/moderation.md:
--------------------------------------------------------------------------------
1 | # Moderation
2 |
3 | Since we don't require user sign in to comment, all comments are NOT displayed by default, until the moderator approve it.
4 |
5 | Cusdis provides a moderate dashboard, let you manage all the comments.
6 |
7 | 
8 |
--------------------------------------------------------------------------------
/public/doc/features/notification.md:
--------------------------------------------------------------------------------
1 | # Notification
2 |
3 | ## Hosted Service
4 |
5 | Our [hosted service](https://cusdis.com/dashboard) comes with Email notification:
6 |
7 | 
8 |
9 | As you can see in the mail content, you can even approve the comment without login.
10 |
11 | ### Disable notification by project
12 |
13 | You can disable notification for specific project in `Websites` -> `Project` -> `Settings`:
14 |
15 | 
16 |
17 | ### Notification Preferences
18 |
19 | You can change notification preferences (such as changing notification email address) in `User` -> `Settings`:
20 |
21 | 
22 |
23 | ## Self-host
24 |
25 | To enable Email notification in self-host Cusdis, you need to set SMTP configuration in environment variables:
26 |
27 | - `SMTP_HOST` **required** SMTP host
28 | - `SMTP_USER` **required** SMTP username
29 | - `SMTP_PASSWORD` **required** SMTP password
30 | - `SMTP_SENDER` **required** sender email address
31 | - `SMTP_PORT` default: 587 SMTP port
32 | - `SMTP_SECURE` default: `true` enable SMTP secure
33 |
34 | > Remember to set `HOST` to your own domain name, in order to get the correct approve link and unsubscribe link in the Email content
35 |
36 | ### SMTP Configuration Examples
37 |
38 | #### Gmail
39 |
40 | First, visit [Google Account Security](https://myaccount.google.com/security) and make sure you have enabled the Two-factor Authentication.
41 | Then, go to [application passwords](https://myaccount.google.com/apppasswords) and create a new password for Cusdis. The configurations would be as following:
42 |
43 | ```
44 | SMTP_HOST=smtp.gmail.com
45 | SMTP_PORT=465
46 | SMTP_SECURE=true
47 | SMTP_USER=your gmail email
48 | SMTP_PASSWORD=
49 | SMTP_SENDER=your gmail email
50 | ```
51 |
52 | > The sender email MUST be the same as login user, but you can give it a display name by `John Doe `, the same applies for other SMTP services.
53 |
54 | #### QQ mail
55 |
56 | Follow [the help page](http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256) to generate an authorization code, also make sure you have enabled the IMAP/SMTP service. The configurations would be as following:
57 |
58 | ```
59 | SMTP_HOST=smtp.qq.com
60 | SMTP_PORT=465
61 | SMTP_SECURE=true
62 | SMTP_USER=your qq mail
63 | SMTP_PASSWORD=
64 | SMTP_SENDER=your qq mail
65 | ```
66 |
67 | #### 163 mail
68 |
69 | Similarly, you need an authorization code to use SMTP service, follow [this page](https://help.mail.163.com/faqDetail.do?code=d7a5dc8471cd0c0e8b4b8f4f8e49998b374173cfe9171305fa1ce630d7f67ac2cda80145a1742516) to create one. The configurations would be as following:
70 |
71 | ```
72 | SMTP_HOST=smtp.163.com
73 | SMTP_PORT=465
74 | SMTP_SECURE=true
75 | SMTP_USER=your 163 mail
76 | SMTP_PASSWORD=
77 | SMTP_SENDER=your 163 mail
78 | ```
79 |
--------------------------------------------------------------------------------
/public/doc/images/advance-notification-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/advance-notification-settings.png
--------------------------------------------------------------------------------
/public/doc/images/disable-notification-in-project.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/disable-notification-in-project.png
--------------------------------------------------------------------------------
/public/doc/images/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/email.png
--------------------------------------------------------------------------------
/public/doc/images/enable_webhook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/enable_webhook.png
--------------------------------------------------------------------------------
/public/doc/images/notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/notification.png
--------------------------------------------------------------------------------
/public/doc/images/pull-based-notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/pull-based-notification.png
--------------------------------------------------------------------------------
/public/doc/images/redeploy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/redeploy.png
--------------------------------------------------------------------------------
/public/doc/images/webhook-notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/webhook-notification.png
--------------------------------------------------------------------------------
/public/doc/images/y3FkAY.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/djyde/cusdis/7bcf25611de75f52b337a9bb2e6b3f931822f56c/public/doc/images/y3FkAY.png
--------------------------------------------------------------------------------
/public/doc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Cusdis Documentation
9 |
10 |
11 |
12 |
13 |
17 |
18 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/public/doc/integration/docsify.md:
--------------------------------------------------------------------------------
1 | # Integrate Cusdis into Docsify
2 |
3 | [Docsify](https://docsify.js.org) is a powerful document site generator, which also powers this Cusdis document. Cusdis has a built-in Docsify plugin.
4 |
5 | ## Usage
6 |
7 | ```html
8 |
16 |
17 |
18 |
19 | ```
20 |
21 |
22 |
--------------------------------------------------------------------------------
/public/doc/integration/jekyll.md:
--------------------------------------------------------------------------------
1 | # Integrate Cusdis into Jekyll
2 |
3 | [Jekyll](https://jekyllrb.com/) is a blog-aware static site generator in Ruby.
4 |
5 | ## Usage
6 |
7 | ```html
8 | {%- if page.id -%}
9 |
11 |
18 |
19 | {%- endif -%}
20 | ```
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/doc/integration/mkdocs.md:
--------------------------------------------------------------------------------
1 | # Integrate Cusdis into Mkdocs
2 |
3 | [MkDocs](https://www.mkdocs.org/) is a **fast**, **simple** and **convenient** static site generator geared towards building project documentation. Documentation source files are written in Markdown, and configured with a single YAML configuration file.
4 |
5 | [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) is the most commonly used theme for [MkDocs](https://www.mkdocs.org/).
6 |
7 | Once you have [Python](https://www.python.org/) installed on your system, as well as [pip](https://pip.readthedocs.io/en/stable/installing/), you can easily install mkdocs with material-mkdocs and start building your own site. For more detail, follow the documentation of [Getting started](https://squidfunk.github.io/mkdocs-material/getting-started/)
8 |
9 | ## Usage
10 |
11 | Here's the tutorial for integrating Cusdis into [Material for MkDocs] following its [comment system configuratin](https://squidfunk.github.io/mkdocs-material/setup/adding-a-comment-system/). As for other [themes](https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes), you can achieve it in a similar way!
12 |
13 | ### Configure the `mkdocs.yml`
14 |
15 | We need add more kv pairs to `mkdocs.yml` with [`extra`](https://www.mkdocs.org/user-guide/configuration/#extra) setting:
16 |
17 | ```yaml
18 | extra:
19 | disqus:
20 | cusdis:
21 | host:
22 | app_id:
23 | lang:
24 | ```
25 |
26 | The `lang` setting aims to support [cusdis i18n](../advanced/i18n.md?id=current-support-language).
27 |
28 | ### Rewrite the template
29 |
30 | We need first extend the theme and [override the `disqus block`](https://squidfunk.github.io/mkdocs-material/customization/#extending-the-theme) to support Cusdis comment system.
31 |
32 | Inorder to override, we can replace it with a file of the same `disqus.html` name and locate in the `overrides directory`:
33 |
34 | ```txt
35 | .
36 | ├─ overrides/
37 | │ └─ partials/
38 | | └─ partials/
39 | │ └─ disqus.html
40 | └─ mkdocs.yml
41 | ```
42 |
43 | Add the following line to `disqus.html`:
44 |
45 | ```html
46 | {% set cusdis = config.extra.cusdis %}
47 | {% if page and page.meta and page.meta.cusdis is string %}
48 | {% set cusdis = page.meta.cusdis %}
49 | {% endif %}
50 | {% if not page.is_homepage %}
51 |