commit
c74b71163a
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
secret.env
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"godotenv"
|
||||
]
|
||||
}
|
||||
4
enshi/.prettierrc
Normal file
4
enshi/.prettierrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": false
|
||||
}
|
||||
50
enshi/README.md
Normal file
50
enshi/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import react from 'eslint-plugin-react'
|
||||
|
||||
export default tseslint.config({
|
||||
// Set the react version
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
// Add the react plugin
|
||||
react,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended rules
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
},
|
||||
})
|
||||
```
|
||||
28
enshi/eslint.config.js
Normal file
28
enshi/eslint.config.js
Normal file
@ -0,0 +1,28 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
21
enshi/index.html
Normal file
21
enshi/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Enshi</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
6974
enshi/package-lock.json
generated
Normal file
6974
enshi/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
49
enshi/package.json
Normal file
49
enshi/package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "enshi",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.1",
|
||||
"@radix-ui/react-form": "^0.1.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@radix-ui/themes": "^3.1.3",
|
||||
"@tanstack/react-query": "^5.55.0",
|
||||
"html-react-parser": "^5.1.16",
|
||||
"i18n": "^0.15.1",
|
||||
"i18next": "^23.14.0",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"jotai": "^2.9.3",
|
||||
"primereact": "^10.8.2",
|
||||
"quill": "^2.0.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^15.0.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-router-dom": "^6.26.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.9.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.9",
|
||||
"globals": "^15.9.0",
|
||||
"postcss": "^8.4.45",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript-eslint": "^8.0.1",
|
||||
"vite": "^5.4.1"
|
||||
}
|
||||
}
|
||||
6
enshi/postcss.config.js
Normal file
6
enshi/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
1
enshi/public/vite.svg
Normal file
1
enshi/public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
966
enshi/src/App.css
Normal file
966
enshi/src/App.css
Normal file
@ -0,0 +1,966 @@
|
||||
.controls {
|
||||
display: flex;
|
||||
border: 1px solid #ccc;
|
||||
border-top: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.controls-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.state {
|
||||
margin: 10px 0;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.state-title {
|
||||
color: #999;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Quill Editor v1.3.6
|
||||
* https://quilljs.com/
|
||||
* Copyright (c) 2014, Jason Chen
|
||||
* Copyright (c) 2013, salesforce.com
|
||||
*/
|
||||
.ql-container {
|
||||
box-sizing: border-box;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
position: relative;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-tooltip {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-clipboard {
|
||||
left: -100000px;
|
||||
height: 1px;
|
||||
overflow-y: hidden;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
.ql-clipboard p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ql-editor {
|
||||
box-sizing: border-box;
|
||||
line-height: 1.42;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
padding: 12px 15px;
|
||||
tab-size: 4;
|
||||
-moz-tab-size: 4;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.ql-editor > * {
|
||||
cursor: text;
|
||||
}
|
||||
.ql-editor p,
|
||||
.ql-editor ol,
|
||||
.ql-editor ul,
|
||||
.ql-editor pre,
|
||||
.ql-editor blockquote,
|
||||
.ql-editor h1,
|
||||
.ql-editor h2,
|
||||
.ql-editor h3,
|
||||
.ql-editor h4,
|
||||
.ql-editor h5,
|
||||
.ql-editor h6 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol,
|
||||
.ql-editor ul {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol > li,
|
||||
.ql-editor ul > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
.ql-editor ul > li::before {
|
||||
content: '\2022';
|
||||
}
|
||||
.ql-editor ul[data-checked=true],
|
||||
.ql-editor ul[data-checked=false] {
|
||||
pointer-events: none;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li *,
|
||||
.ql-editor ul[data-checked=false] > li * {
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li::before,
|
||||
.ql-editor ul[data-checked=false] > li::before {
|
||||
color: #777;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
.ql-editor ul[data-checked=true] > li::before {
|
||||
content: '\2611';
|
||||
}
|
||||
.ql-editor ul[data-checked=false] > li::before {
|
||||
content: '\2610';
|
||||
}
|
||||
.ql-editor li::before {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
width: 1.2em;
|
||||
}
|
||||
.ql-editor li:not(.ql-direction-rtl)::before {
|
||||
margin-left: -1.5em;
|
||||
margin-right: 0.3em;
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor li.ql-direction-rtl::before {
|
||||
margin-left: 0.3em;
|
||||
margin-right: -1.5em;
|
||||
}
|
||||
.ql-editor ol li:not(.ql-direction-rtl),
|
||||
.ql-editor ul li:not(.ql-direction-rtl) {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
.ql-editor ol li.ql-direction-rtl,
|
||||
.ql-editor ul li.ql-direction-rtl {
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
.ql-editor ol li {
|
||||
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
counter-increment: list-0;
|
||||
}
|
||||
.ql-editor ol li:before {
|
||||
content: counter(list-0, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-increment: list-1;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1:before {
|
||||
content: counter(list-1, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-1 {
|
||||
counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-increment: list-2;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2:before {
|
||||
content: counter(list-2, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-2 {
|
||||
counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-increment: list-3;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3:before {
|
||||
content: counter(list-3, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-3 {
|
||||
counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-increment: list-4;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4:before {
|
||||
content: counter(list-4, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-4 {
|
||||
counter-reset: list-5 list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-increment: list-5;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5:before {
|
||||
content: counter(list-5, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-5 {
|
||||
counter-reset: list-6 list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-increment: list-6;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6:before {
|
||||
content: counter(list-6, decimal) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-6 {
|
||||
counter-reset: list-7 list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-increment: list-7;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7:before {
|
||||
content: counter(list-7, lower-alpha) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-7 {
|
||||
counter-reset: list-8 list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-increment: list-8;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8:before {
|
||||
content: counter(list-8, lower-roman) '. ';
|
||||
}
|
||||
.ql-editor ol li.ql-indent-8 {
|
||||
counter-reset: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9 {
|
||||
counter-increment: list-9;
|
||||
}
|
||||
.ql-editor ol li.ql-indent-9:before {
|
||||
content: counter(list-9, decimal) '. ';
|
||||
}
|
||||
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
|
||||
padding-left: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 3em;
|
||||
}
|
||||
.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 4.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
|
||||
padding-left: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 6em;
|
||||
}
|
||||
.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 7.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
|
||||
padding-left: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 9em;
|
||||
}
|
||||
.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 10.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
|
||||
padding-left: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 12em;
|
||||
}
|
||||
.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 13.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
|
||||
padding-left: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 15em;
|
||||
}
|
||||
.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 16.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
|
||||
padding-left: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 18em;
|
||||
}
|
||||
.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 19.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
|
||||
padding-left: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 21em;
|
||||
}
|
||||
.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 22.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
|
||||
padding-left: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 24em;
|
||||
}
|
||||
.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 25.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
|
||||
padding-left: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 27em;
|
||||
}
|
||||
.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
|
||||
padding-right: 28.5em;
|
||||
}
|
||||
.ql-editor .ql-video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-center {
|
||||
margin: 0 auto;
|
||||
}
|
||||
.ql-editor .ql-video.ql-align-right {
|
||||
margin: 0 0 0 auto;
|
||||
}
|
||||
.ql-editor .ql-bg-black {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-editor .ql-bg-red {
|
||||
background-color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-bg-orange {
|
||||
background-color: #f90;
|
||||
}
|
||||
.ql-editor .ql-bg-yellow {
|
||||
background-color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-bg-green {
|
||||
background-color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-bg-blue {
|
||||
background-color: #06c;
|
||||
}
|
||||
.ql-editor .ql-bg-purple {
|
||||
background-color: #93f;
|
||||
}
|
||||
.ql-editor .ql-color-white {
|
||||
color: #fff;
|
||||
}
|
||||
.ql-editor .ql-color-red {
|
||||
color: #e60000;
|
||||
}
|
||||
.ql-editor .ql-color-orange {
|
||||
color: #f90;
|
||||
}
|
||||
.ql-editor .ql-color-yellow {
|
||||
color: #ff0;
|
||||
}
|
||||
.ql-editor .ql-color-green {
|
||||
color: #008a00;
|
||||
}
|
||||
.ql-editor .ql-color-blue {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-editor .ql-color-purple {
|
||||
color: #93f;
|
||||
}
|
||||
.ql-editor .ql-font-serif {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-editor .ql-font-monospace {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-editor .ql-size-small {
|
||||
font-size: 0.75em;
|
||||
}
|
||||
.ql-editor .ql-size-large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-editor .ql-size-huge {
|
||||
font-size: 2.5em;
|
||||
}
|
||||
.ql-editor .ql-direction-rtl {
|
||||
direction: rtl;
|
||||
text-align: inherit;
|
||||
}
|
||||
.ql-editor .ql-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.ql-editor .ql-align-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
.ql-editor .ql-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.ql-editor.ql-blank::before {
|
||||
color: rgba(0,0,0,0.6);
|
||||
content: attr(data-placeholder);
|
||||
font-style: italic;
|
||||
left: 15px;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
}
|
||||
.ql-snow.ql-toolbar:after,
|
||||
.ql-snow .ql-toolbar:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-snow.ql-toolbar button,
|
||||
.ql-snow .ql-toolbar button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 24px;
|
||||
padding: 3px 5px;
|
||||
width: 28px;
|
||||
}
|
||||
.ql-snow.ql-toolbar button svg,
|
||||
.ql-snow .ql-toolbar button svg {
|
||||
float: left;
|
||||
height: 100%;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:active:hover,
|
||||
.ql-snow .ql-toolbar button:active:hover {
|
||||
outline: none;
|
||||
}
|
||||
.ql-snow.ql-toolbar input.ql-image[type=file],
|
||||
.ql-snow .ql-toolbar input.ql-image[type=file] {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover,
|
||||
.ql-snow .ql-toolbar button:hover,
|
||||
.ql-snow.ql-toolbar button:focus,
|
||||
.ql-snow .ql-toolbar button:focus,
|
||||
.ql-snow.ql-toolbar button.ql-active,
|
||||
.ql-snow .ql-toolbar button.ql-active,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar button:focus .ql-fill,
|
||||
.ql-snow .ql-toolbar button:focus .ql-fill,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
|
||||
fill: #06c;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:focus .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
|
||||
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
|
||||
stroke: #06c;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active),
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) {
|
||||
color: #444;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
|
||||
fill: #444;
|
||||
}
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
|
||||
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
|
||||
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
|
||||
stroke: #444;
|
||||
}
|
||||
}
|
||||
.ql-snow {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-snow * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ql-snow .ql-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-out-bottom,
|
||||
.ql-snow .ql-out-top {
|
||||
visibility: hidden;
|
||||
}
|
||||
.ql-snow .ql-tooltip {
|
||||
position: absolute;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
.ql-snow .ql-tooltip a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-flip {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
.ql-snow .ql-formats {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-snow .ql-formats:after {
|
||||
clear: both;
|
||||
content: '';
|
||||
display: table;
|
||||
}
|
||||
.ql-snow .ql-stroke {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-snow .ql-stroke-miter {
|
||||
fill: none;
|
||||
stroke: #444;
|
||||
stroke-miterlimit: 10;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.ql-snow .ql-fill,
|
||||
.ql-snow .ql-stroke.ql-fill {
|
||||
fill: #444;
|
||||
}
|
||||
.ql-snow .ql-empty {
|
||||
fill: none;
|
||||
}
|
||||
.ql-snow .ql-even {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
.ql-snow .ql-thin,
|
||||
.ql-snow .ql-stroke.ql-thin {
|
||||
stroke-width: 1;
|
||||
}
|
||||
.ql-snow .ql-transparent {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.ql-snow .ql-direction svg:last-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-direction.ql-active svg:last-child {
|
||||
display: inline;
|
||||
}
|
||||
.ql-snow .ql-direction.ql-active svg:first-child {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-editor h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-snow .ql-editor h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-snow .ql-editor h3 {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-snow .ql-editor h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-snow .ql-editor h5 {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-snow .ql-editor h6 {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-snow .ql-editor a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.ql-snow .ql-editor blockquote {
|
||||
border-left: 4px solid #ccc;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
.ql-snow .ql-editor code,
|
||||
.ql-snow .ql-editor pre {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.ql-snow .ql-editor pre {
|
||||
white-space: pre-wrap;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.ql-snow .ql-editor code {
|
||||
font-size: 85%;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-editor pre.ql-syntax {
|
||||
background-color: #23241f;
|
||||
color: #f8f8f2;
|
||||
overflow: visible;
|
||||
}
|
||||
.ql-snow .ql-editor img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.ql-snow .ql-picker {
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.ql-snow .ql-picker-label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
padding-left: 8px;
|
||||
padding-right: 2px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.ql-snow .ql-picker-label::before {
|
||||
display: inline-block;
|
||||
line-height: 22px;
|
||||
}
|
||||
.ql-snow .ql-picker-options {
|
||||
background-color: #fff;
|
||||
display: none;
|
||||
min-width: 100%;
|
||||
padding: 4px 8px;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-snow .ql-picker-options .ql-picker-item {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
||||
color: #ccc;
|
||||
z-index: 2;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
|
||||
fill: #ccc;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
|
||||
stroke: #ccc;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-expanded .ql-picker-options {
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
top: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.ql-snow .ql-color-picker,
|
||||
.ql-snow .ql-icon-picker {
|
||||
width: 28px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-label,
|
||||
.ql-snow .ql-icon-picker .ql-picker-label {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-label svg,
|
||||
.ql-snow .ql-icon-picker .ql-picker-label svg {
|
||||
right: 4px;
|
||||
}
|
||||
.ql-snow .ql-icon-picker .ql-picker-options {
|
||||
padding: 4px 0px;
|
||||
}
|
||||
.ql-snow .ql-icon-picker .ql-picker-item {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-options {
|
||||
padding: 3px 5px;
|
||||
width: 152px;
|
||||
}
|
||||
.ql-snow .ql-color-picker .ql-picker-item {
|
||||
border: 1px solid transparent;
|
||||
float: left;
|
||||
height: 16px;
|
||||
margin: 2px;
|
||||
padding: 0px;
|
||||
width: 16px;
|
||||
}
|
||||
.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
|
||||
position: absolute;
|
||||
margin-top: -9px;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 18px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {
|
||||
content: attr(data-label);
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
content: 'Heading 1';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
content: 'Heading 2';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
content: 'Heading 3';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
content: 'Heading 4';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
content: 'Heading 5';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
content: 'Heading 6';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
font-size: 2em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
font-size: 1.17em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
font-size: 0.83em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
font-size: 0.67em;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font {
|
||||
width: 108px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: 'Sans Serif';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
content: 'Serif';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
content: 'Monospace';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
|
||||
font-family: Georgia, Times New Roman, serif;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
|
||||
font-family: Monaco, Courier New, monospace;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size {
|
||||
width: 98px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: 'Normal';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
content: 'Small';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
content: 'Large';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
content: 'Huge';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
|
||||
font-size: 10px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
|
||||
font-size: 18px;
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
|
||||
font-size: 32px;
|
||||
}
|
||||
.ql-snow .ql-color-picker.ql-background .ql-picker-item {
|
||||
background-color: #fff;
|
||||
}
|
||||
.ql-snow .ql-color-picker.ql-color .ql-picker-item {
|
||||
background-color: #000;
|
||||
}
|
||||
.ql-toolbar.ql-snow {
|
||||
border: 1px solid #ccc;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
|
||||
padding: 8px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-formats {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker-label {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker-options {
|
||||
border: 1px solid transparent;
|
||||
box-shadow: rgba(0,0,0,0.2) 0 2px 8px;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
|
||||
border-color: #ccc;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
|
||||
border-color: #ccc;
|
||||
}
|
||||
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,
|
||||
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {
|
||||
border-color: #000;
|
||||
}
|
||||
.ql-toolbar.ql-snow + .ql-container.ql-snow {
|
||||
border-top: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0px 0px 5px #ddd;
|
||||
color: #444;
|
||||
padding: 5px 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ql-snow .ql-tooltip::before {
|
||||
content: "Visit URL:";
|
||||
line-height: 26px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip input[type=text] {
|
||||
display: none;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
height: 26px;
|
||||
margin: 0px;
|
||||
padding: 3px 5px;
|
||||
width: 170px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-preview {
|
||||
display: inline-block;
|
||||
max-width: 200px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-action::after {
|
||||
border-right: 1px solid #ccc;
|
||||
content: 'Edit';
|
||||
margin-left: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a.ql-remove::before {
|
||||
content: 'Remove';
|
||||
margin-left: 8px;
|
||||
}
|
||||
.ql-snow .ql-tooltip a {
|
||||
line-height: 26px;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-preview,
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-remove {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing input[type=text] {
|
||||
display: inline-block;
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||
border-right: 0px;
|
||||
content: 'Save';
|
||||
padding-right: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=link]::before {
|
||||
content: "Enter link:";
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=formula]::before {
|
||||
content: "Enter formula:";
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode=video]::before {
|
||||
content: "Enter video:";
|
||||
}
|
||||
.ql-snow a {
|
||||
color: #06c;
|
||||
}
|
||||
.ql-container.ql-snow {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
42
enshi/src/App.tsx
Normal file
42
enshi/src/App.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import "./App.css";
|
||||
import "@radix-ui/themes/styles.css";
|
||||
import { Theme, ThemePanel } from "@radix-ui/themes";
|
||||
|
||||
import { createBrowserRouter, RouterProvider } from "react-router-dom";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import queryClient from "./api/QueryClient/QueryClient";
|
||||
import { routes } from "./routes/routes";
|
||||
import { useEffect } from "react";
|
||||
import "axios";
|
||||
import { axiosLocalhost } from "./api/axios/axios";
|
||||
|
||||
const router = createBrowserRouter(routes);
|
||||
|
||||
export default function App() {
|
||||
useEffect(() => {
|
||||
let f = async () => {
|
||||
let c = await axiosLocalhost.post(
|
||||
"/login",
|
||||
{
|
||||
nickname: "StasikChess",
|
||||
password: "123456",
|
||||
}
|
||||
);
|
||||
|
||||
console.log(c.headers);
|
||||
console.log(document.cookie);
|
||||
};
|
||||
|
||||
f();
|
||||
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Theme className="h-fit" accentColor="indigo" grayColor="slate">
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
<ThemePanel />
|
||||
</QueryClientProvider>
|
||||
</Theme>
|
||||
);
|
||||
}
|
||||
13
enshi/src/Components/ArticleViewer/ArcticleViewer.tsx
Normal file
13
enshi/src/Components/ArticleViewer/ArcticleViewer.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
import { Container } from "@radix-ui/themes";
|
||||
import React from "react";
|
||||
|
||||
export default function ArcticleViewer() {
|
||||
return (
|
||||
<>
|
||||
<div className="ql-snow">
|
||||
<Container className="mt-4 ql-editor">
|
||||
</Container>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
80
enshi/src/Components/Editor/Editor.tsx
Normal file
80
enshi/src/Components/Editor/Editor.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
import Quill, { Delta, } from "quill/core";
|
||||
import ReactQuill from "react-quill";
|
||||
import React, {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import Sources from "quill";
|
||||
|
||||
type TEditor = {
|
||||
readOnly?: boolean;
|
||||
defaultValue?: string | Delta;
|
||||
onChange: (d: string) => void; // TODO: make type
|
||||
onSelectionChange?: any; // TODO same as before
|
||||
};
|
||||
|
||||
const modules = {
|
||||
toolbar: [
|
||||
[{ header: [1, 2, 3, false] }],
|
||||
["bold", "italic", "underline", "strike", "blockquote"],
|
||||
[
|
||||
{ list: "ordered" },
|
||||
{ list: "bullet" },
|
||||
{ indent: "-1" },
|
||||
{ indent: "+1" },
|
||||
],
|
||||
["link", "image"],
|
||||
["clean"],
|
||||
[{ align: [] }],
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* @param onChange - function that accepts Delta element
|
||||
*/
|
||||
const Editor = forwardRef((props: TEditor) => {
|
||||
const editor = useRef(null);
|
||||
const [quill, setQuill] = useState<Quill | null>(null);
|
||||
const [value, setValue] = useState(new Delta())
|
||||
|
||||
useEffect(() => {
|
||||
if (editor.current) {
|
||||
//@ts-ignore
|
||||
const temp = editor.current.getEditor() as Quill;
|
||||
setQuill(temp);
|
||||
}
|
||||
return () => {
|
||||
setQuill(null);
|
||||
};
|
||||
}, [editor.current]);
|
||||
|
||||
const changeHandler = (val: string, _changeDelta: Delta, _source: Sources, _editor: ReactQuill.UnprivilegedEditor) => {
|
||||
console.log(val);
|
||||
console.log(JSON.stringify(quill?.getContents().ops, null, 2))
|
||||
let fullDelta = quill?.getContents()
|
||||
props.onChange(val || "")
|
||||
setValue(fullDelta || new Delta())
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-editor">
|
||||
<ReactQuill
|
||||
value={value}
|
||||
ref={editor}
|
||||
modules={modules}
|
||||
|
||||
|
||||
onChange={changeHandler}
|
||||
|
||||
|
||||
theme="snow"
|
||||
placeholder="Type your thoughts here..."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default Editor;
|
||||
67
enshi/src/Components/NavBar/NavBar.tsx
Normal file
67
enshi/src/Components/NavBar/NavBar.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import { Button, Card, ChevronDownIcon, Text } from "@radix-ui/themes";
|
||||
import * as NavigationMenu from "@radix-ui/react-navigation-menu";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
export default function NavBar() {
|
||||
return (
|
||||
<nav className="pt-2">
|
||||
<NavigationMenu.Root
|
||||
orientation="horizontal"
|
||||
className="flex justify-center"
|
||||
>
|
||||
<NavigationMenu.List className="flex justify-center gap-2">
|
||||
<NavItem text="Cringer" to="/" />
|
||||
|
||||
<NavItem text="C-Cringer" to="/c" />
|
||||
|
||||
<NavigationMenu.Item className="text-center">
|
||||
<NavigationMenu.Trigger className="flex items-center">
|
||||
<Button
|
||||
asChild
|
||||
className="w-fit pr-2 h-fit rounded-full m-0 p-0 pl-2 mt-2 mb-2 duration-[50ms]"
|
||||
variant="ghost"
|
||||
highContrast
|
||||
>
|
||||
<Text
|
||||
size={"3"}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
Cringer 123 <ChevronDownIcon />
|
||||
</Text>
|
||||
</Button>
|
||||
</NavigationMenu.Trigger>
|
||||
|
||||
<NavigationMenu.Content className="absolute data-[motion=from-start]:scale-150">
|
||||
<Card>asd</Card>
|
||||
</NavigationMenu.Content>
|
||||
</NavigationMenu.Item>
|
||||
</NavigationMenu.List>
|
||||
</NavigationMenu.Root>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
type TNavItem = {
|
||||
text: string;
|
||||
to: string;
|
||||
};
|
||||
|
||||
function NavItem(props: TNavItem) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<NavigationMenu.Item>
|
||||
<NavigationMenu.Link>
|
||||
<Button
|
||||
className="w-fit h-fit rounded-full m-0 p-0 pr-2 pl-2 mt-2 mb-2 duration-[50ms]"
|
||||
highContrast
|
||||
variant={location.pathname === props.to ? "solid" : "ghost"}
|
||||
onClick={() => navigate(props.to)}
|
||||
>
|
||||
<Text size={"3"}>{props.text}</Text>
|
||||
</Button>
|
||||
</NavigationMenu.Link>
|
||||
</NavigationMenu.Item>
|
||||
);
|
||||
}
|
||||
23
enshi/src/Pages/MainPage/MainPage.tsx
Normal file
23
enshi/src/Pages/MainPage/MainPage.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from 'react'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import NavBar from '../../Components/NavBar/NavBar'
|
||||
import { axiosLocalhost } from '../../api/axios/axios'
|
||||
|
||||
export default function MainPage() {
|
||||
return (
|
||||
<>
|
||||
<NavBar />
|
||||
<Outlet />
|
||||
<button
|
||||
onClick={
|
||||
async () => {
|
||||
let d = await axiosLocalhost.get("getCookie")
|
||||
console.log(d.data);
|
||||
|
||||
}
|
||||
}>
|
||||
qwpofjqwifhqwuif
|
||||
</button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
14
enshi/src/api/QueryClient/QueryClient.ts
Normal file
14
enshi/src/api/QueryClient/QueryClient.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {
|
||||
QueryClient
|
||||
} from '@tanstack/react-query'
|
||||
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: Infinity
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default queryClient
|
||||
13
enshi/src/api/axios/axios.ts
Normal file
13
enshi/src/api/axios/axios.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import axios from "axios";
|
||||
|
||||
export const axiosLocalhost = axios.create(
|
||||
{
|
||||
baseURL: `http://localhost:9876/`,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
axios.defaults.withCredentials = true;
|
||||
1
enshi/src/assets/react.svg
Normal file
1
enshi/src/assets/react.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
17
enshi/src/index.css
Normal file
17
enshi/src/index.css
Normal file
@ -0,0 +1,17 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.radix-themes {
|
||||
--default-font-family:
|
||||
--heading-font-family:
|
||||
/* Your custom font for <Heading> components */
|
||||
--code-font-family:
|
||||
/* Your custom font for <Code> components */
|
||||
--strong-font-family:
|
||||
/* Your custom font for <Strong> components */
|
||||
--em-font-family:
|
||||
/* Your custom font for <Em> components */
|
||||
--quote-font-family:
|
||||
/* Your custom font for <Quote> components */;
|
||||
}
|
||||
5
enshi/src/locale/en.ts
Normal file
5
enshi/src/locale/en.ts
Normal file
@ -0,0 +1,5 @@
|
||||
const en = {
|
||||
hello: "hello!"
|
||||
}
|
||||
|
||||
export default en;
|
||||
28
enshi/src/locale/i18n.ts
Normal file
28
enshi/src/locale/i18n.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import ru from "./ru";
|
||||
import en from "./en";
|
||||
|
||||
i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
fallbackLng: "ru",
|
||||
lng: "ru",
|
||||
debug: true,
|
||||
|
||||
resources: {
|
||||
ru: {
|
||||
translation: {...ru},
|
||||
},
|
||||
|
||||
en: {
|
||||
translation: {...en},
|
||||
},
|
||||
},
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
5
enshi/src/locale/ru.ts
Normal file
5
enshi/src/locale/ru.ts
Normal file
@ -0,0 +1,5 @@
|
||||
const ru = {
|
||||
hello: "Привет!"
|
||||
}
|
||||
|
||||
export default ru;
|
||||
11
enshi/src/main.tsx
Normal file
11
enshi/src/main.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App.tsx'
|
||||
import './index.css'
|
||||
import './locale/i18n.ts'
|
||||
import i18n from './locale/i18n.ts'
|
||||
|
||||
i18n.changeLanguage(navigator.language)
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<App />
|
||||
)
|
||||
27
enshi/src/routes/routes.tsx
Normal file
27
enshi/src/routes/routes.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { createRoutesFromElements, Route, useRouteError } from "react-router-dom"
|
||||
import MainPage from "../Pages/MainPage/MainPage"
|
||||
import {Text} from "@radix-ui/themes";
|
||||
|
||||
|
||||
function ErrorBoundary() {
|
||||
let error = useRouteError();
|
||||
console.error(error);
|
||||
|
||||
return <div>Dang! This route does not exist... Yet ;)</div>;
|
||||
}
|
||||
|
||||
export const routes = createRoutesFromElements(
|
||||
<>
|
||||
<Route
|
||||
path="/"
|
||||
errorElement={<ErrorBoundary />}
|
||||
element={<MainPage />}
|
||||
>
|
||||
<Route index element={<Text>Cringer path</Text>} />
|
||||
<Route
|
||||
path="/a?/c"
|
||||
element={<Text>Cringer path, but this a</Text>}
|
||||
></Route>
|
||||
</Route>
|
||||
</>
|
||||
)
|
||||
1
enshi/src/vite-env.d.ts
vendored
Normal file
1
enshi/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
21
enshi/tailwind.config.js
Normal file
21
enshi/tailwind.config.js
Normal file
@ -0,0 +1,21 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
animation: {
|
||||
'appear': 'appear 0.25s'
|
||||
},
|
||||
keyframes: {
|
||||
appear: {
|
||||
'100%': {opacity: '1'}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
0
enshi/test
Normal file
0
enshi/test
Normal file
24
enshi/tsconfig.app.json
Normal file
24
enshi/tsconfig.app.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
enshi/tsconfig.json
Normal file
7
enshi/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
22
enshi/tsconfig.node.json
Normal file
22
enshi/tsconfig.node.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
7
enshi/vite.config.ts
Normal file
7
enshi/vite.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
42
enshi_back/REST_API_stuff/requestAnswers.go
Normal file
42
enshi_back/REST_API_stuff/requestAnswers.go
Normal file
@ -0,0 +1,42 @@
|
||||
package rest_api_stuff
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func OkAnswer(c *gin.Context, message string) {
|
||||
c.IndentedJSON(
|
||||
http.StatusOK,
|
||||
gin.H{"message": message},
|
||||
)
|
||||
}
|
||||
|
||||
func BadRequestAnswer(c *gin.Context, err error) {
|
||||
c.IndentedJSON(
|
||||
http.StatusBadRequest,
|
||||
gin.H{"error": err.Error()},
|
||||
)
|
||||
}
|
||||
|
||||
func InternalErrorAnswer(c *gin.Context, err error) {
|
||||
c.IndentedJSON(
|
||||
http.StatusInternalServerError,
|
||||
gin.H{"error": err.Error()},
|
||||
)
|
||||
}
|
||||
|
||||
func ConflictAnswer(c *gin.Context, err error) {
|
||||
c.IndentedJSON(
|
||||
http.StatusConflict,
|
||||
gin.H{"error": err.Error()},
|
||||
)
|
||||
}
|
||||
|
||||
func UnauthorizedAnswer(c *gin.Context, err error) {
|
||||
c.IndentedJSON(
|
||||
http.StatusUnauthorized,
|
||||
gin.H{"error": err.Error()},
|
||||
)
|
||||
}
|
||||
27
enshi_back/REST_API_stuff/setCookie.go
Normal file
27
enshi_back/REST_API_stuff/setCookie.go
Normal file
@ -0,0 +1,27 @@
|
||||
package rest_api_stuff
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type CookieParams struct {
|
||||
Name string
|
||||
Value string
|
||||
MaxAge int
|
||||
Path string
|
||||
Domain string
|
||||
Secure bool
|
||||
HttpOnly bool
|
||||
}
|
||||
|
||||
func SetCookie(c *gin.Context, params *CookieParams) {
|
||||
c.SetCookie(
|
||||
params.Name,
|
||||
params.Value,
|
||||
params.MaxAge,
|
||||
params.Path,
|
||||
params.Domain,
|
||||
params.Secure,
|
||||
params.HttpOnly,
|
||||
)
|
||||
}
|
||||
86
enshi_back/auth/jwt.go
Normal file
86
enshi_back/auth/jwt.go
Normal file
@ -0,0 +1,86 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
var (
|
||||
// Secret key to sign JWT-tokens
|
||||
SecretKey string
|
||||
)
|
||||
|
||||
type UserInfoJWT struct {
|
||||
Id int64
|
||||
Username string
|
||||
IsAdmin bool
|
||||
}
|
||||
|
||||
// Generating new token with user info
|
||||
//
|
||||
// userInfo = { "id": int, "name": string }
|
||||
func CreateToken(userInfo UserInfoJWT) (string, error) {
|
||||
// Create new token
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
|
||||
// Get claims of this token (payload)
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
|
||||
// Add some info to claims
|
||||
claims["username"] = userInfo.Username
|
||||
claims["id"] = strconv.FormatInt(userInfo.Id, 10)
|
||||
claims["isAdmin"] = userInfo.IsAdmin
|
||||
claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
|
||||
|
||||
// Get string token that will be passed to user
|
||||
// We sign this token with SecretKey
|
||||
tokenString, err := token.SignedString([]byte(SecretKey))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error generate token: " + err.Error())
|
||||
}
|
||||
|
||||
return tokenString, nil
|
||||
}
|
||||
|
||||
// # Returns (claims, nil) if token is valid and all good
|
||||
//
|
||||
// # Returns (nil, error) if token is invalid for some reason
|
||||
//
|
||||
// Claims consists of name, id, exp(expiration time)
|
||||
func ValidateToken(tokenSting string) (jwt.MapClaims, error) {
|
||||
// Parsing string version of token to *jwt.Token
|
||||
token, err := jwt.Parse(
|
||||
// First arg -> string to parse
|
||||
tokenSting,
|
||||
|
||||
// Second arg -> function that check hash method of token
|
||||
// Return Secret key with what token string gonna be checked
|
||||
func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("wrong hash method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return []byte(SecretKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error in token: %v", err.Error())
|
||||
}
|
||||
|
||||
// Check token expiration time and if it is valid
|
||||
// Get claims from parsed token
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
expirationTime := time.Unix(int64(claims["exp"].(float64)), 0)
|
||||
|
||||
if expirationTime.Before(time.Now()) {
|
||||
return nil, fmt.Errorf("token has expired")
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid token")
|
||||
}
|
||||
}
|
||||
123
enshi_back/db/go_queries/blogs_queries.sql.go
Normal file
123
enshi_back/db/go_queries/blogs_queries.sql.go
Normal file
@ -0,0 +1,123 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: blogs_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createBlogByUserId = `-- name: CreateBlogByUserId :one
|
||||
INSERT INTO public.blogs
|
||||
(blog_id, user_id, title, description, category_id, created_at)
|
||||
VALUES($1, $2, $3, $4, $5, CURRENT_TIMESTAMP)
|
||||
RETURNING blog_id, user_id, title, description, category_id, created_at
|
||||
`
|
||||
|
||||
type CreateBlogByUserIdParams struct {
|
||||
BlogID int64 `json:"blog_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
CategoryID pgtype.Int4 `json:"category_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBlogByUserId(ctx context.Context, arg CreateBlogByUserIdParams) (Blog, error) {
|
||||
row := q.db.QueryRow(ctx, createBlogByUserId,
|
||||
arg.BlogID,
|
||||
arg.UserID,
|
||||
arg.Title,
|
||||
arg.Description,
|
||||
arg.CategoryID,
|
||||
)
|
||||
var i Blog
|
||||
err := row.Scan(
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.CategoryID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteBlogByBlogId = `-- name: DeleteBlogByBlogId :exec
|
||||
DELETE FROM public.blogs
|
||||
WHERE blog_id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteBlogByBlogId(ctx context.Context, blogID int64) error {
|
||||
_, err := q.db.Exec(ctx, deleteBlogByBlogId, blogID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getBlogsByUserId = `-- name: GetBlogsByUserId :many
|
||||
SELECT blog_id, user_id, title, description, category_id, created_at
|
||||
FROM public.blogs
|
||||
WHERE user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetBlogsByUserId(ctx context.Context, userID int64) ([]Blog, error) {
|
||||
rows, err := q.db.Query(ctx, getBlogsByUserId, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Blog
|
||||
for rows.Next() {
|
||||
var i Blog
|
||||
if err := rows.Scan(
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.CategoryID,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateBlogInfoByBlogId = `-- name: UpdateBlogInfoByBlogId :one
|
||||
UPDATE public.blogs
|
||||
SET title=$1, description=$2, category_id=$3
|
||||
WHERE blog_id=$4
|
||||
RETURNING blog_id, user_id, title, description, category_id, created_at
|
||||
`
|
||||
|
||||
type UpdateBlogInfoByBlogIdParams struct {
|
||||
Title pgtype.Text `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
CategoryID pgtype.Int4 `json:"category_id"`
|
||||
BlogID int64 `json:"blog_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateBlogInfoByBlogId(ctx context.Context, arg UpdateBlogInfoByBlogIdParams) (Blog, error) {
|
||||
row := q.db.QueryRow(ctx, updateBlogInfoByBlogId,
|
||||
arg.Title,
|
||||
arg.Description,
|
||||
arg.CategoryID,
|
||||
arg.BlogID,
|
||||
)
|
||||
var i Blog
|
||||
err := row.Scan(
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Description,
|
||||
&i.CategoryID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
103
enshi_back/db/go_queries/bookmarks_queries.sql.go
Normal file
103
enshi_back/db/go_queries/bookmarks_queries.sql.go
Normal file
@ -0,0 +1,103 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: bookmarks_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createBookmark = `-- name: CreateBookmark :one
|
||||
INSERT INTO public.bookmarks
|
||||
(user_id, post_id, bookmarked_at)
|
||||
VALUES($1, $2, CURRENT_TIMESTAMP)
|
||||
RETURNING user_id, post_id, bookmarked_at
|
||||
`
|
||||
|
||||
type CreateBookmarkParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
PostID int64 `json:"post_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateBookmark(ctx context.Context, arg CreateBookmarkParams) (Bookmark, error) {
|
||||
row := q.db.QueryRow(ctx, createBookmark, arg.UserID, arg.PostID)
|
||||
var i Bookmark
|
||||
err := row.Scan(&i.UserID, &i.PostID, &i.BookmarkedAt)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteBookmark = `-- name: DeleteBookmark :exec
|
||||
DELETE FROM public.bookmarks
|
||||
WHERE user_id=$1 AND post_id=$2
|
||||
`
|
||||
|
||||
type DeleteBookmarkParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
PostID int64 `json:"post_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteBookmark(ctx context.Context, arg DeleteBookmarkParams) error {
|
||||
_, err := q.db.Exec(ctx, deleteBookmark, arg.UserID, arg.PostID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getBookmarkTimestamp = `-- name: GetBookmarkTimestamp :one
|
||||
SELECT bookmarked_at
|
||||
FROM public.bookmarks bookmarks
|
||||
where bookmarks.post_id = $1 and bookmarks.user_id = $2
|
||||
`
|
||||
|
||||
type GetBookmarkTimestampParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetBookmarkTimestamp(ctx context.Context, arg GetBookmarkTimestampParams) (pgtype.Timestamp, error) {
|
||||
row := q.db.QueryRow(ctx, getBookmarkTimestamp, arg.PostID, arg.UserID)
|
||||
var bookmarked_at pgtype.Timestamp
|
||||
err := row.Scan(&bookmarked_at)
|
||||
return bookmarked_at, err
|
||||
}
|
||||
|
||||
const getBookmarksByUserId = `-- name: GetBookmarksByUserId :many
|
||||
SELECT user_id, post_id, bookmarked_at
|
||||
FROM public.bookmarks bookmarks
|
||||
where bookmarks.user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetBookmarksByUserId(ctx context.Context, userID int64) ([]Bookmark, error) {
|
||||
rows, err := q.db.Query(ctx, getBookmarksByUserId, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Bookmark
|
||||
for rows.Next() {
|
||||
var i Bookmark
|
||||
if err := rows.Scan(&i.UserID, &i.PostID, &i.BookmarkedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCountOfBookmarksByPostId = `-- name: GetCountOfBookmarksByPostId :one
|
||||
SELECT COUNT(*)
|
||||
FROM public.bookmarks bookmarks
|
||||
where bookmarks.post_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCountOfBookmarksByPostId(ctx context.Context, postID int64) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, getCountOfBookmarksByPostId, postID)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
74
enshi_back/db/go_queries/categories_queries.sql.go
Normal file
74
enshi_back/db/go_queries/categories_queries.sql.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: categories_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCategory = `-- name: CreateCategory :one
|
||||
INSERT INTO public.categories
|
||||
(category_id, category_name)
|
||||
VALUES($1, $2)
|
||||
RETURNING category_id, category_name
|
||||
`
|
||||
|
||||
type CreateCategoryParams struct {
|
||||
CategoryID int32 `json:"category_id"`
|
||||
CategoryName string `json:"category_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCategory(ctx context.Context, arg CreateCategoryParams) (Category, error) {
|
||||
row := q.db.QueryRow(ctx, createCategory, arg.CategoryID, arg.CategoryName)
|
||||
var i Category
|
||||
err := row.Scan(&i.CategoryID, &i.CategoryName)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteCategoryById = `-- name: DeleteCategoryById :exec
|
||||
DELETE FROM public.categories
|
||||
WHERE category_id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteCategoryById(ctx context.Context, categoryID int32) error {
|
||||
_, err := q.db.Exec(ctx, deleteCategoryById, categoryID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllCategories = `-- name: GetAllCategories :many
|
||||
SELECT category_id, category_name FROM public.categories
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllCategories(ctx context.Context) ([]Category, error) {
|
||||
rows, err := q.db.Query(ctx, getAllCategories)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Category
|
||||
for rows.Next() {
|
||||
var i Category
|
||||
if err := rows.Scan(&i.CategoryID, &i.CategoryName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCategoryByName = `-- name: GetCategoryByName :one
|
||||
SELECT category_id, category_name FROM public.categories WHERE category_name = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCategoryByName(ctx context.Context, categoryName string) (Category, error) {
|
||||
row := q.db.QueryRow(ctx, getCategoryByName, categoryName)
|
||||
var i Category
|
||||
err := row.Scan(&i.CategoryID, &i.CategoryName)
|
||||
return i, err
|
||||
}
|
||||
181
enshi_back/db/go_queries/comments_queries.sql.go
Normal file
181
enshi_back/db/go_queries/comments_queries.sql.go
Normal file
@ -0,0 +1,181 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: comments_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createComment = `-- name: CreateComment :one
|
||||
INSERT INTO public."comments"
|
||||
(comment_id, post_id, user_id, "content", created_at)
|
||||
VALUES($1, $2, $3, $4, CURRENT_TIMESTAMP)
|
||||
RETURNING comment_id, post_id, user_id, content, created_at
|
||||
`
|
||||
|
||||
type CreateCommentParams struct {
|
||||
CommentID int64 `json:"comment_id"`
|
||||
PostID pgtype.Int8 `json:"post_id"`
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateComment(ctx context.Context, arg CreateCommentParams) (Comment, error) {
|
||||
row := q.db.QueryRow(ctx, createComment,
|
||||
arg.CommentID,
|
||||
arg.PostID,
|
||||
arg.UserID,
|
||||
arg.Content,
|
||||
)
|
||||
var i Comment
|
||||
err := row.Scan(
|
||||
&i.CommentID,
|
||||
&i.PostID,
|
||||
&i.UserID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteComment = `-- name: DeleteComment :exec
|
||||
DELETE FROM public."comments"
|
||||
WHERE comment_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteComment(ctx context.Context, commentID int64) error {
|
||||
_, err := q.db.Exec(ctx, deleteComment, commentID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCommentByUserId = `-- name: GetCommentByUserId :one
|
||||
SELECT comment_id, post_id, user_id, "content", created_at
|
||||
FROM public."comments"
|
||||
where public."comments".user_id = $1 and public."comments".post_id = $2
|
||||
`
|
||||
|
||||
type GetCommentByUserIdParams struct {
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
PostID pgtype.Int8 `json:"post_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCommentByUserId(ctx context.Context, arg GetCommentByUserIdParams) (Comment, error) {
|
||||
row := q.db.QueryRow(ctx, getCommentByUserId, arg.UserID, arg.PostID)
|
||||
var i Comment
|
||||
err := row.Scan(
|
||||
&i.CommentID,
|
||||
&i.PostID,
|
||||
&i.UserID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCommentsForPostAsc = `-- name: GetCommentsForPostAsc :many
|
||||
SELECT comment_id, post_id, user_id, "content", created_at
|
||||
FROM public."comments"
|
||||
where public."comments".post_id = $1
|
||||
order by created_at ASC
|
||||
LIMIT 10 offset ($2 * 10)
|
||||
`
|
||||
|
||||
type GetCommentsForPostAscParams struct {
|
||||
PostID pgtype.Int8 `json:"post_id"`
|
||||
Column2 interface{} `json:"column_2"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCommentsForPostAsc(ctx context.Context, arg GetCommentsForPostAscParams) ([]Comment, error) {
|
||||
rows, err := q.db.Query(ctx, getCommentsForPostAsc, arg.PostID, arg.Column2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Comment
|
||||
for rows.Next() {
|
||||
var i Comment
|
||||
if err := rows.Scan(
|
||||
&i.CommentID,
|
||||
&i.PostID,
|
||||
&i.UserID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCommentsForPostDesc = `-- name: GetCommentsForPostDesc :many
|
||||
SELECT comment_id, post_id, user_id, "content", created_at
|
||||
FROM public."comments"
|
||||
where public."comments".post_id = $1
|
||||
order by created_at DESC
|
||||
LIMIT 10 offset ($2 * 10)
|
||||
`
|
||||
|
||||
type GetCommentsForPostDescParams struct {
|
||||
PostID pgtype.Int8 `json:"post_id"`
|
||||
Column2 interface{} `json:"column_2"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCommentsForPostDesc(ctx context.Context, arg GetCommentsForPostDescParams) ([]Comment, error) {
|
||||
rows, err := q.db.Query(ctx, getCommentsForPostDesc, arg.PostID, arg.Column2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Comment
|
||||
for rows.Next() {
|
||||
var i Comment
|
||||
if err := rows.Scan(
|
||||
&i.CommentID,
|
||||
&i.PostID,
|
||||
&i.UserID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateCommentByCommentId = `-- name: UpdateCommentByCommentId :one
|
||||
UPDATE public."comments"
|
||||
SET "content"=$2
|
||||
WHERE comment_id=$1
|
||||
RETURNING comment_id, post_id, user_id, content, created_at
|
||||
`
|
||||
|
||||
type UpdateCommentByCommentIdParams struct {
|
||||
CommentID int64 `json:"comment_id"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCommentByCommentId(ctx context.Context, arg UpdateCommentByCommentIdParams) (Comment, error) {
|
||||
row := q.db.QueryRow(ctx, updateCommentByCommentId, arg.CommentID, arg.Content)
|
||||
var i Comment
|
||||
err := row.Scan(
|
||||
&i.CommentID,
|
||||
&i.PostID,
|
||||
&i.UserID,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
32
enshi_back/db/go_queries/db.go
Normal file
32
enshi_back/db/go_queries/db.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type DBTX interface {
|
||||
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
|
||||
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
|
||||
QueryRow(context.Context, string, ...interface{}) pgx.Row
|
||||
}
|
||||
|
||||
func New(db DBTX) *Queries {
|
||||
return &Queries{db: db}
|
||||
}
|
||||
|
||||
type Queries struct {
|
||||
db DBTX
|
||||
}
|
||||
|
||||
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
|
||||
return &Queries{
|
||||
db: tx,
|
||||
}
|
||||
}
|
||||
44
enshi_back/db/go_queries/favorites_queries.sql.go
Normal file
44
enshi_back/db/go_queries/favorites_queries.sql.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: favorites_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createFavorite = `-- name: CreateFavorite :one
|
||||
INSERT INTO public.favorites
|
||||
(user_id, blog_id, favorited_at)
|
||||
VALUES($1, $2, CURRENT_TIMESTAMP)
|
||||
RETURNING user_id, blog_id, favorited_at
|
||||
`
|
||||
|
||||
type CreateFavoriteParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
BlogID int64 `json:"blog_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateFavorite(ctx context.Context, arg CreateFavoriteParams) (Favorite, error) {
|
||||
row := q.db.QueryRow(ctx, createFavorite, arg.UserID, arg.BlogID)
|
||||
var i Favorite
|
||||
err := row.Scan(&i.UserID, &i.BlogID, &i.FavoritedAt)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteFavorite = `-- name: DeleteFavorite :exec
|
||||
DELETE FROM public.favorites
|
||||
WHERE user_id=$1 AND blog_id=$2
|
||||
`
|
||||
|
||||
type DeleteFavoriteParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
BlogID int64 `json:"blog_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteFavorite(ctx context.Context, arg DeleteFavoriteParams) error {
|
||||
_, err := q.db.Exec(ctx, deleteFavorite, arg.UserID, arg.BlogID)
|
||||
return err
|
||||
}
|
||||
83
enshi_back/db/go_queries/likes_queries.sql.go
Normal file
83
enshi_back/db/go_queries/likes_queries.sql.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: likes_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createLike = `-- name: CreateLike :one
|
||||
INSERT INTO public.likes
|
||||
(like_id, user_id, comment_id, created_at)
|
||||
VALUES($1, $2, $3, CURRENT_TIMESTAMP)
|
||||
RETURNING like_id, user_id, comment_id, created_at
|
||||
`
|
||||
|
||||
type CreateLikeParams struct {
|
||||
LikeID int64 `json:"like_id"`
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
CommentID pgtype.Int8 `json:"comment_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateLike(ctx context.Context, arg CreateLikeParams) (Like, error) {
|
||||
row := q.db.QueryRow(ctx, createLike, arg.LikeID, arg.UserID, arg.CommentID)
|
||||
var i Like
|
||||
err := row.Scan(
|
||||
&i.LikeID,
|
||||
&i.UserID,
|
||||
&i.CommentID,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteLikeByUserCommentId = `-- name: DeleteLikeByUserCommentId :exec
|
||||
DELETE FROM public.likes
|
||||
WHERE user_id = $1 and comment_id = $2
|
||||
`
|
||||
|
||||
type DeleteLikeByUserCommentIdParams struct {
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
CommentID pgtype.Int8 `json:"comment_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteLikeByUserCommentId(ctx context.Context, arg DeleteLikeByUserCommentIdParams) error {
|
||||
_, err := q.db.Exec(ctx, deleteLikeByUserCommentId, arg.UserID, arg.CommentID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getLikesForComment = `-- name: GetLikesForComment :one
|
||||
SELECT count(*)
|
||||
FROM public.likes
|
||||
WHERE comment_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLikesForComment(ctx context.Context, commentID pgtype.Int8) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, getLikesForComment, commentID)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
const isUserLikedComment = `-- name: IsUserLikedComment :one
|
||||
SELECT count(*)
|
||||
FROM public.likes
|
||||
WHERE user_id = $1 and comment_id = $2
|
||||
`
|
||||
|
||||
type IsUserLikedCommentParams struct {
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
CommentID pgtype.Int8 `json:"comment_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) IsUserLikedComment(ctx context.Context, arg IsUserLikedCommentParams) (int64, error) {
|
||||
row := q.db.QueryRow(ctx, isUserLikedComment, arg.UserID, arg.CommentID)
|
||||
var count int64
|
||||
err := row.Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
92
enshi_back/db/go_queries/models.go
Normal file
92
enshi_back/db/go_queries/models.go
Normal file
@ -0,0 +1,92 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type Blog struct {
|
||||
BlogID int64 `json:"blog_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Description pgtype.Text `json:"description"`
|
||||
CategoryID pgtype.Int4 `json:"category_id"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
}
|
||||
|
||||
type Bookmark struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
PostID int64 `json:"post_id"`
|
||||
BookmarkedAt pgtype.Timestamp `json:"bookmarked_at"`
|
||||
}
|
||||
|
||||
type Category struct {
|
||||
CategoryID int32 `json:"category_id"`
|
||||
CategoryName string `json:"category_name"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
CommentID int64 `json:"comment_id"`
|
||||
PostID pgtype.Int8 `json:"post_id"`
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
}
|
||||
|
||||
type Favorite struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
BlogID int64 `json:"blog_id"`
|
||||
FavoritedAt pgtype.Timestamp `json:"favorited_at"`
|
||||
}
|
||||
|
||||
type Like struct {
|
||||
LikeID int64 `json:"like_id"`
|
||||
UserID pgtype.Int8 `json:"user_id"`
|
||||
CommentID pgtype.Int8 `json:"comment_id"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
}
|
||||
|
||||
type Post struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
BlogID pgtype.Int8 `json:"blog_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||
}
|
||||
|
||||
type PostTag struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
TagID int32 `json:"tag_id"`
|
||||
}
|
||||
|
||||
type PostVote struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Vote bool `json:"vote"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
AvatarUrl pgtype.Text `json:"avatar_url"`
|
||||
WebsiteUrl pgtype.Text `json:"website_url"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
TagID int32 `json:"tag_id"`
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
48
enshi_back/db/go_queries/multi_queries.sql.go
Normal file
48
enshi_back/db/go_queries/multi_queries.sql.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: multi_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getFavoriteBlogsByUserId = `-- name: GetFavoriteBlogsByUserId :many
|
||||
SELECT blogs.blog_id, blogs.user_id, blogs.title, blogs.description, blogs.category_id, blogs.created_at
|
||||
FROM favorites
|
||||
JOIN blogs on blogs.blog_id = favorites.blog_id
|
||||
WHERE favorites.user_id = $1
|
||||
`
|
||||
|
||||
type GetFavoriteBlogsByUserIdRow struct {
|
||||
Blog Blog `json:"blog"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetFavoriteBlogsByUserId(ctx context.Context, userID int64) ([]GetFavoriteBlogsByUserIdRow, error) {
|
||||
rows, err := q.db.Query(ctx, getFavoriteBlogsByUserId, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetFavoriteBlogsByUserIdRow
|
||||
for rows.Next() {
|
||||
var i GetFavoriteBlogsByUserIdRow
|
||||
if err := rows.Scan(
|
||||
&i.Blog.BlogID,
|
||||
&i.Blog.UserID,
|
||||
&i.Blog.Title,
|
||||
&i.Blog.Description,
|
||||
&i.Blog.CategoryID,
|
||||
&i.Blog.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
75
enshi_back/db/go_queries/post_tags_queries.sql.go
Normal file
75
enshi_back/db/go_queries/post_tags_queries.sql.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: post_tags_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createPostTagRelation = `-- name: CreatePostTagRelation :one
|
||||
INSERT INTO public.post_tags
|
||||
(post_id, tag_id)
|
||||
VALUES($1, $2)
|
||||
RETURNING post_id, tag_id
|
||||
`
|
||||
|
||||
type CreatePostTagRelationParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
TagID int32 `json:"tag_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreatePostTagRelation(ctx context.Context, arg CreatePostTagRelationParams) (PostTag, error) {
|
||||
row := q.db.QueryRow(ctx, createPostTagRelation, arg.PostID, arg.TagID)
|
||||
var i PostTag
|
||||
err := row.Scan(&i.PostID, &i.TagID)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deletePostTagRelation = `-- name: DeletePostTagRelation :exec
|
||||
DELETE FROM public.post_tags
|
||||
WHERE post_id = $1 AND tag_id = $2
|
||||
`
|
||||
|
||||
type DeletePostTagRelationParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
TagID int32 `json:"tag_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeletePostTagRelation(ctx context.Context, arg DeletePostTagRelationParams) error {
|
||||
_, err := q.db.Exec(ctx, deletePostTagRelation, arg.PostID, arg.TagID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllTagsForPost = `-- name: GetAllTagsForPost :many
|
||||
SELECT tags.tag_id, tags.tag_name
|
||||
from public.tags tags
|
||||
JOIN public.post_tags post_tags on post_tags.tag_id = tags.tag_id
|
||||
JOIN public.posts posts on posts.post_id = post_tags.post_id
|
||||
`
|
||||
|
||||
type GetAllTagsForPostRow struct {
|
||||
Tag Tag `json:"tag"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllTagsForPost(ctx context.Context) ([]GetAllTagsForPostRow, error) {
|
||||
rows, err := q.db.Query(ctx, getAllTagsForPost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetAllTagsForPostRow
|
||||
for rows.Next() {
|
||||
var i GetAllTagsForPostRow
|
||||
if err := rows.Scan(&i.Tag.TagID, &i.Tag.TagName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
83
enshi_back/db/go_queries/post_votes_queries.sql.go
Normal file
83
enshi_back/db/go_queries/post_votes_queries.sql.go
Normal file
@ -0,0 +1,83 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: post_votes_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createPostVote = `-- name: CreatePostVote :one
|
||||
INSERT INTO public.post_votes
|
||||
(post_id, user_id, vote)
|
||||
VALUES($1, $2, $3)
|
||||
RETURNING post_id, user_id, vote
|
||||
`
|
||||
|
||||
type CreatePostVoteParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Vote bool `json:"vote"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreatePostVote(ctx context.Context, arg CreatePostVoteParams) (PostVote, error) {
|
||||
row := q.db.QueryRow(ctx, createPostVote, arg.PostID, arg.UserID, arg.Vote)
|
||||
var i PostVote
|
||||
err := row.Scan(&i.PostID, &i.UserID, &i.Vote)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deletePostVote = `-- name: DeletePostVote :exec
|
||||
DELETE FROM public.post_votes
|
||||
WHERE post_id=$1 AND user_id=$2
|
||||
`
|
||||
|
||||
type DeletePostVoteParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeletePostVote(ctx context.Context, arg DeletePostVoteParams) error {
|
||||
_, err := q.db.Exec(ctx, deletePostVote, arg.PostID, arg.UserID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getPostVote = `-- name: GetPostVote :one
|
||||
SELECT vote
|
||||
FROM public.post_votes p_v
|
||||
WHERE p_v.user_id = $1 and p_v.post_id = $2
|
||||
`
|
||||
|
||||
type GetPostVoteParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
PostID int64 `json:"post_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetPostVote(ctx context.Context, arg GetPostVoteParams) (bool, error) {
|
||||
row := q.db.QueryRow(ctx, getPostVote, arg.UserID, arg.PostID)
|
||||
var vote bool
|
||||
err := row.Scan(&vote)
|
||||
return vote, err
|
||||
}
|
||||
|
||||
const updateVote = `-- name: UpdateVote :one
|
||||
UPDATE public.post_votes
|
||||
SET vote=$1
|
||||
WHERE post_id=$2 AND user_id=$3
|
||||
RETURNING post_id, user_id, vote
|
||||
`
|
||||
|
||||
type UpdateVoteParams struct {
|
||||
Vote bool `json:"vote"`
|
||||
PostID int64 `json:"post_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateVote(ctx context.Context, arg UpdateVoteParams) (PostVote, error) {
|
||||
row := q.db.QueryRow(ctx, updateVote, arg.Vote, arg.PostID, arg.UserID)
|
||||
var i PostVote
|
||||
err := row.Scan(&i.PostID, &i.UserID, &i.Vote)
|
||||
return i, err
|
||||
}
|
||||
183
enshi_back/db/go_queries/posts_queries.sql.go
Normal file
183
enshi_back/db/go_queries/posts_queries.sql.go
Normal file
@ -0,0 +1,183 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: posts_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createPost = `-- name: CreatePost :one
|
||||
INSERT INTO public.posts
|
||||
(post_id, blog_id, user_id, title, "content", created_at, updated_at)
|
||||
VALUES($1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreatePostParams struct {
|
||||
PostID int64 `json:"post_id"`
|
||||
BlogID pgtype.Int8 `json:"blog_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreatePost(ctx context.Context, arg CreatePostParams) (Post, error) {
|
||||
row := q.db.QueryRow(ctx, createPost,
|
||||
arg.PostID,
|
||||
arg.BlogID,
|
||||
arg.UserID,
|
||||
arg.Title,
|
||||
arg.Content,
|
||||
)
|
||||
var i Post
|
||||
err := row.Scan(
|
||||
&i.PostID,
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deletePostByPostId = `-- name: DeletePostByPostId :exec
|
||||
DELETE FROM public.posts
|
||||
WHERE post_id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeletePostByPostId(ctx context.Context, postID int64) error {
|
||||
_, err := q.db.Exec(ctx, deletePostByPostId, postID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getPostsByBlogId = `-- name: GetPostsByBlogId :many
|
||||
SELECT post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
FROM public.posts posts
|
||||
where posts.blog_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetPostsByBlogId(ctx context.Context, blogID pgtype.Int8) ([]Post, error) {
|
||||
rows, err := q.db.Query(ctx, getPostsByBlogId, blogID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Post
|
||||
for rows.Next() {
|
||||
var i Post
|
||||
if err := rows.Scan(
|
||||
&i.PostID,
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getPostsByPostId = `-- name: GetPostsByPostId :one
|
||||
SELECT post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
FROM public.posts posts
|
||||
where posts.post_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetPostsByPostId(ctx context.Context, postID int64) (Post, error) {
|
||||
row := q.db.QueryRow(ctx, getPostsByPostId, postID)
|
||||
var i Post
|
||||
err := row.Scan(
|
||||
&i.PostID,
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getPostsByUserId = `-- name: GetPostsByUserId :many
|
||||
SELECT post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
FROM public.posts posts
|
||||
where posts.user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetPostsByUserId(ctx context.Context, userID int64) ([]Post, error) {
|
||||
rows, err := q.db.Query(ctx, getPostsByUserId, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Post
|
||||
for rows.Next() {
|
||||
var i Post
|
||||
if err := rows.Scan(
|
||||
&i.PostID,
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updatePostByPostId = `-- name: UpdatePostByPostId :one
|
||||
UPDATE public.posts
|
||||
SET blog_id=$1, user_id=$2, title=$3, "content"=$4, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $5
|
||||
RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdatePostByPostIdParams struct {
|
||||
BlogID pgtype.Int8 `json:"blog_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Title pgtype.Text `json:"title"`
|
||||
Content pgtype.Text `json:"content"`
|
||||
PostID int64 `json:"post_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePostByPostId(ctx context.Context, arg UpdatePostByPostIdParams) (Post, error) {
|
||||
row := q.db.QueryRow(ctx, updatePostByPostId,
|
||||
arg.BlogID,
|
||||
arg.UserID,
|
||||
arg.Title,
|
||||
arg.Content,
|
||||
arg.PostID,
|
||||
)
|
||||
var i Post
|
||||
err := row.Scan(
|
||||
&i.PostID,
|
||||
&i.BlogID,
|
||||
&i.UserID,
|
||||
&i.Title,
|
||||
&i.Content,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
107
enshi_back/db/go_queries/profiles_queries.sql.go
Normal file
107
enshi_back/db/go_queries/profiles_queries.sql.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: profiles_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const clearProfileByUserId = `-- name: ClearProfileByUserId :one
|
||||
UPDATE public.profiles
|
||||
SET bio='', avatar_url='', website_url=''
|
||||
WHERE user_id=$1
|
||||
RETURNING user_id, bio, avatar_url, website_url
|
||||
`
|
||||
|
||||
func (q *Queries) ClearProfileByUserId(ctx context.Context, userID int64) (Profile, error) {
|
||||
row := q.db.QueryRow(ctx, clearProfileByUserId, userID)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Bio,
|
||||
&i.AvatarUrl,
|
||||
&i.WebsiteUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createProfileForUser = `-- name: CreateProfileForUser :one
|
||||
INSERT INTO public.profiles
|
||||
(user_id, bio, avatar_url, website_url)
|
||||
VALUES($1, '', '', '')
|
||||
RETURNING user_id, bio, avatar_url, website_url
|
||||
`
|
||||
|
||||
func (q *Queries) CreateProfileForUser(ctx context.Context, userID int64) (Profile, error) {
|
||||
row := q.db.QueryRow(ctx, createProfileForUser, userID)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Bio,
|
||||
&i.AvatarUrl,
|
||||
&i.WebsiteUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteProfileByUserId = `-- name: DeleteProfileByUserId :exec
|
||||
DELETE FROM public.profiles
|
||||
WHERE user_id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteProfileByUserId(ctx context.Context, userID int64) error {
|
||||
_, err := q.db.Exec(ctx, deleteProfileByUserId, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getProfileByUserId = `-- name: GetProfileByUserId :one
|
||||
SELECT user_id, bio, avatar_url, website_url FROM public.profiles WHERE user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetProfileByUserId(ctx context.Context, userID int64) (Profile, error) {
|
||||
row := q.db.QueryRow(ctx, getProfileByUserId, userID)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Bio,
|
||||
&i.AvatarUrl,
|
||||
&i.WebsiteUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateProfileByUserId = `-- name: UpdateProfileByUserId :one
|
||||
UPDATE public.profiles
|
||||
SET bio=$2, avatar_url=$3, website_url=$4
|
||||
WHERE user_id=$1
|
||||
RETURNING user_id, bio, avatar_url, website_url
|
||||
`
|
||||
|
||||
type UpdateProfileByUserIdParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Bio pgtype.Text `json:"bio"`
|
||||
AvatarUrl pgtype.Text `json:"avatar_url"`
|
||||
WebsiteUrl pgtype.Text `json:"website_url"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateProfileByUserId(ctx context.Context, arg UpdateProfileByUserIdParams) (Profile, error) {
|
||||
row := q.db.QueryRow(ctx, updateProfileByUserId,
|
||||
arg.UserID,
|
||||
arg.Bio,
|
||||
arg.AvatarUrl,
|
||||
arg.WebsiteUrl,
|
||||
)
|
||||
var i Profile
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Bio,
|
||||
&i.AvatarUrl,
|
||||
&i.WebsiteUrl,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
78
enshi_back/db/go_queries/tags_queries.sql.go
Normal file
78
enshi_back/db/go_queries/tags_queries.sql.go
Normal file
@ -0,0 +1,78 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: tags_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createTag = `-- name: CreateTag :one
|
||||
INSERT INTO public.tags
|
||||
(tag_id, tag_name)
|
||||
VALUES($1, $2)
|
||||
RETURNING tag_id, tag_name
|
||||
`
|
||||
|
||||
type CreateTagParams struct {
|
||||
TagID int32 `json:"tag_id"`
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateTag(ctx context.Context, arg CreateTagParams) (Tag, error) {
|
||||
row := q.db.QueryRow(ctx, createTag, arg.TagID, arg.TagName)
|
||||
var i Tag
|
||||
err := row.Scan(&i.TagID, &i.TagName)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteTag = `-- name: DeleteTag :exec
|
||||
DELETE FROM public.tags
|
||||
WHERE tag_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteTag(ctx context.Context, tagID int32) error {
|
||||
_, err := q.db.Exec(ctx, deleteTag, tagID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllTags = `-- name: GetAllTags :many
|
||||
SELECT tag_id, tag_name
|
||||
FROM public.tags
|
||||
ORDER BY tag_name ASC
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllTags(ctx context.Context) ([]Tag, error) {
|
||||
rows, err := q.db.Query(ctx, getAllTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Tag
|
||||
for rows.Next() {
|
||||
var i Tag
|
||||
if err := rows.Scan(&i.TagID, &i.TagName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getTagByTagId = `-- name: GetTagByTagId :one
|
||||
SELECT tag_id, tag_name
|
||||
FROM public.tags tags
|
||||
where tags.tag_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetTagByTagId(ctx context.Context, tagID int32) (Tag, error) {
|
||||
row := q.db.QueryRow(ctx, getTagByTagId, tagID)
|
||||
var i Tag
|
||||
err := row.Scan(&i.TagID, &i.TagName)
|
||||
return i, err
|
||||
}
|
||||
179
enshi_back/db/go_queries/users_queries.sql.go
Normal file
179
enshi_back/db/go_queries/users_queries.sql.go
Normal file
@ -0,0 +1,179 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// source: users_queries.sql
|
||||
|
||||
package db_repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO public.users
|
||||
(user_id, username, email, "password", created_at, is_admin)
|
||||
VALUES($1, $2, $3, $4, CURRENT_TIMESTAMP, false)
|
||||
RETURNING user_id, username, email, password, created_at, is_admin
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, createUser,
|
||||
arg.UserID,
|
||||
arg.Username,
|
||||
arg.Email,
|
||||
arg.Password,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteUserById = `-- name: DeleteUserById :exec
|
||||
DELETE FROM public.users
|
||||
WHERE user_id=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUserById(ctx context.Context, userID int64) error {
|
||||
_, err := q.db.Exec(ctx, deleteUserById, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteUserByUsername = `-- name: DeleteUserByUsername :exec
|
||||
DELETE FROM public.users
|
||||
WHERE username=$1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUserByUsername(ctx context.Context, username string) error {
|
||||
_, err := q.db.Exec(ctx, deleteUserByUsername, username)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllUsers = `-- name: GetAllUsers :many
|
||||
SELECT user_id, username, email, password, created_at, is_admin FROM users
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
|
||||
rows, err := q.db.Query(ctx, getAllUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
for rows.Next() {
|
||||
var i User
|
||||
if err := rows.Scan(
|
||||
&i.UserID,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getUserByEmailOrNickname = `-- name: GetUserByEmailOrNickname :one
|
||||
SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE username = $1 OR email = $2 LIMIT 1
|
||||
`
|
||||
|
||||
type GetUserByEmailOrNicknameParams struct {
|
||||
Username string `json:"username" validate:"required"`
|
||||
Email string `json:"email" validate:"required,email"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByEmailOrNickname(ctx context.Context, arg GetUserByEmailOrNicknameParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByEmailOrNickname, arg.Username, arg.Email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserById = `-- name: GetUserById :one
|
||||
SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE user_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserById(ctx context.Context, userID int64) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserById, userID)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByUsername = `-- name: GetUserByUsername :one
|
||||
SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE username = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (User, error) {
|
||||
row := q.db.QueryRow(ctx, getUserByUsername, username)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateUserPasswordHash = `-- name: UpdateUserPasswordHash :one
|
||||
UPDATE public.users
|
||||
SET "password"=$1
|
||||
WHERE user_id=$2
|
||||
RETURNING user_id, username, email, password, created_at, is_admin
|
||||
`
|
||||
|
||||
type UpdateUserPasswordHashParams struct {
|
||||
Password string `json:"password" validate:"required"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUserPasswordHash(ctx context.Context, arg UpdateUserPasswordHashParams) (User, error) {
|
||||
row := q.db.QueryRow(ctx, updateUserPasswordHash, arg.Password, arg.UserID)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.UserID,
|
||||
&i.Username,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.CreatedAt,
|
||||
&i.IsAdmin,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
30
enshi_back/db/migrations/migration1.sql
Normal file
30
enshi_back/db/migrations/migration1.sql
Normal file
@ -0,0 +1,30 @@
|
||||
-- Add new schema named "public"
|
||||
CREATE SCHEMA IF NOT EXISTS "public";
|
||||
-- Set comment to schema: "public"
|
||||
COMMENT ON SCHEMA "public" IS 'standard public schema';
|
||||
-- Create "categories" table
|
||||
CREATE TABLE "public"."categories" ("category_id" integer NOT NULL, "category_name" character varying(50) NOT NULL, PRIMARY KEY ("category_id"), CONSTRAINT "categories_category_name_key" UNIQUE ("category_name"));
|
||||
-- Create "users" table
|
||||
CREATE TABLE "public"."users" ("user_id" bigint NOT NULL, "username" character varying(50) NOT NULL, "email" character varying(100) NOT NULL, "password" character varying(255) NOT NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, "is_admin" boolean NOT NULL, PRIMARY KEY ("user_id"), CONSTRAINT "users_email_key" UNIQUE ("email"), CONSTRAINT "users_username_key" UNIQUE ("username"));
|
||||
-- Create "blogs" table
|
||||
CREATE TABLE "public"."blogs" ("blog_id" bigint NOT NULL, "user_id" bigint NOT NULL, "title" character varying(255) NULL, "description" text NULL, "category_id" integer NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("blog_id"), CONSTRAINT "blogs_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "public"."categories" ("category_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "blogs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "posts" table
|
||||
CREATE TABLE "public"."posts" ("post_id" bigint NOT NULL, "blog_id" bigint NULL, "user_id" bigint NOT NULL, "title" character varying(255) NULL, "content" text NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, "updated_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("post_id"), CONSTRAINT "posts_blog_id_fkey" FOREIGN KEY ("blog_id") REFERENCES "public"."blogs" ("blog_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "posts_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "bookmarks" table
|
||||
CREATE TABLE "public"."bookmarks" ("user_id" bigint NOT NULL, "post_id" bigint NOT NULL, "bookmarked_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("user_id", "post_id"), CONSTRAINT "bookmarks_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "bookmarks_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "comments" table
|
||||
CREATE TABLE "public"."comments" ("comment_id" bigint NOT NULL, "post_id" bigint NULL, "user_id" bigint NULL, "content" text NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("comment_id"), CONSTRAINT "comments_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "favorites" table
|
||||
CREATE TABLE "public"."favorites" ("user_id" bigint NOT NULL, "blog_id" bigint NOT NULL, "favorited_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("user_id", "blog_id"), CONSTRAINT "favorites_blog_id_fkey" FOREIGN KEY ("blog_id") REFERENCES "public"."blogs" ("blog_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "favorites_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "likes" table
|
||||
CREATE TABLE "public"."likes" ("like_id" bigint NOT NULL, "user_id" bigint NULL, "comment_id" bigint NULL, "created_at" timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY ("like_id"), CONSTRAINT "likes_comment_id_fkey" FOREIGN KEY ("comment_id") REFERENCES "public"."comments" ("comment_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "likes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "tags" table
|
||||
CREATE TABLE "public"."tags" ("tag_id" integer NOT NULL, "tag_name" character varying(50) NOT NULL, PRIMARY KEY ("tag_id"), CONSTRAINT "tags_tag_name_key" UNIQUE ("tag_name"));
|
||||
-- Create "post_tags" table
|
||||
CREATE TABLE "public"."post_tags" ("post_id" bigint NOT NULL, "tag_id" integer NOT NULL, PRIMARY KEY ("post_id", "tag_id"), CONSTRAINT "post_tags_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE CASCADE, CONSTRAINT "post_tags_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."tags" ("tag_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create "post_votes" table
|
||||
CREATE TABLE "public"."post_votes" ("post_id" bigint NOT NULL, "user_id" bigint NOT NULL, "vote" boolean NOT NULL, PRIMARY KEY ("post_id", "user_id"), CONSTRAINT "post_votes_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "post_votes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE NO ACTION);
|
||||
-- Create "profiles" table
|
||||
CREATE TABLE "public"."profiles" ("user_id" bigint NOT NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("user_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE);
|
||||
-- Create index "profiles_user_id_idx" to table: "profiles"
|
||||
CREATE UNIQUE INDEX "profiles_user_id_idx" ON "public"."profiles" ("user_id");
|
||||
20
enshi_back/db/queries/blogs_queries.sql
Normal file
20
enshi_back/db/queries/blogs_queries.sql
Normal file
@ -0,0 +1,20 @@
|
||||
-- name: CreateBlogByUserId :one
|
||||
INSERT INTO public.blogs
|
||||
(blog_id, user_id, title, description, category_id, created_at)
|
||||
VALUES($1, $2, $3, $4, $5, CURRENT_TIMESTAMP)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateBlogInfoByBlogId :one
|
||||
UPDATE public.blogs
|
||||
SET title=$1, description=$2, category_id=$3
|
||||
WHERE blog_id=$4
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetBlogsByUserId :many
|
||||
SELECT *
|
||||
FROM public.blogs
|
||||
WHERE user_id = $1;
|
||||
|
||||
-- name: DeleteBlogByBlogId :exec
|
||||
DELETE FROM public.blogs
|
||||
WHERE blog_id=$1;
|
||||
24
enshi_back/db/queries/bookmarks_queries.sql
Normal file
24
enshi_back/db/queries/bookmarks_queries.sql
Normal file
@ -0,0 +1,24 @@
|
||||
-- name: GetBookmarksByUserId :many
|
||||
SELECT *
|
||||
FROM public.bookmarks bookmarks
|
||||
where bookmarks.user_id = $1;
|
||||
|
||||
-- name: GetCountOfBookmarksByPostId :one
|
||||
SELECT COUNT(*)
|
||||
FROM public.bookmarks bookmarks
|
||||
where bookmarks.post_id = $1;
|
||||
|
||||
-- name: GetBookmarkTimestamp :one
|
||||
SELECT bookmarked_at
|
||||
FROM public.bookmarks bookmarks
|
||||
where bookmarks.post_id = $1 and bookmarks.user_id = $2;
|
||||
|
||||
-- name: CreateBookmark :one
|
||||
INSERT INTO public.bookmarks
|
||||
(user_id, post_id, bookmarked_at)
|
||||
VALUES($1, $2, CURRENT_TIMESTAMP)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteBookmark :exec
|
||||
DELETE FROM public.bookmarks
|
||||
WHERE user_id=$1 AND post_id=$2;
|
||||
16
enshi_back/db/queries/categories_queries.sql
Normal file
16
enshi_back/db/queries/categories_queries.sql
Normal file
@ -0,0 +1,16 @@
|
||||
-- name: CreateCategory :one
|
||||
INSERT INTO public.categories
|
||||
(category_id, category_name)
|
||||
VALUES($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetAllCategories :many
|
||||
SELECT * FROM public.categories;
|
||||
|
||||
-- name: GetCategoryByName :one
|
||||
SELECT * FROM public.categories WHERE category_name = $1;
|
||||
|
||||
-- name: DeleteCategoryById :exec
|
||||
DELETE FROM public.categories
|
||||
WHERE category_id=$1;
|
||||
|
||||
34
enshi_back/db/queries/comments_queries.sql
Normal file
34
enshi_back/db/queries/comments_queries.sql
Normal file
@ -0,0 +1,34 @@
|
||||
-- name: CreateComment :one
|
||||
INSERT INTO public."comments"
|
||||
(comment_id, post_id, user_id, "content", created_at)
|
||||
VALUES($1, $2, $3, $4, CURRENT_TIMESTAMP)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteComment :exec
|
||||
DELETE FROM public."comments"
|
||||
WHERE comment_id = $1;
|
||||
|
||||
-- name: GetCommentsForPostDesc :many
|
||||
SELECT comment_id, post_id, user_id, "content", created_at
|
||||
FROM public."comments"
|
||||
where public."comments".post_id = $1
|
||||
order by created_at DESC
|
||||
LIMIT 10 offset ($2 * 10);
|
||||
|
||||
-- name: GetCommentsForPostAsc :many
|
||||
SELECT comment_id, post_id, user_id, "content", created_at
|
||||
FROM public."comments"
|
||||
where public."comments".post_id = $1
|
||||
order by created_at ASC
|
||||
LIMIT 10 offset ($2 * 10);
|
||||
|
||||
-- name: UpdateCommentByCommentId :one
|
||||
UPDATE public."comments"
|
||||
SET "content"=$2
|
||||
WHERE comment_id=$1
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetCommentByUserId :one
|
||||
SELECT comment_id, post_id, user_id, "content", created_at
|
||||
FROM public."comments"
|
||||
where public."comments".user_id = $1 and public."comments".post_id = $2;
|
||||
9
enshi_back/db/queries/favorites_queries.sql
Normal file
9
enshi_back/db/queries/favorites_queries.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- name: CreateFavorite :one
|
||||
INSERT INTO public.favorites
|
||||
(user_id, blog_id, favorited_at)
|
||||
VALUES($1, $2, CURRENT_TIMESTAMP)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteFavorite :exec
|
||||
DELETE FROM public.favorites
|
||||
WHERE user_id=$1 AND blog_id=$2;
|
||||
20
enshi_back/db/queries/likes_queries.sql
Normal file
20
enshi_back/db/queries/likes_queries.sql
Normal file
@ -0,0 +1,20 @@
|
||||
-- name: CreateLike :one
|
||||
INSERT INTO public.likes
|
||||
(like_id, user_id, comment_id, created_at)
|
||||
VALUES($1, $2, $3, CURRENT_TIMESTAMP)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteLikeByUserCommentId :exec
|
||||
DELETE FROM public.likes
|
||||
WHERE user_id = $1 and comment_id = $2;
|
||||
|
||||
-- name: GetLikesForComment :one
|
||||
SELECT count(*)
|
||||
FROM public.likes
|
||||
WHERE comment_id = $1;
|
||||
|
||||
-- name: IsUserLikedComment :one
|
||||
SELECT count(*)
|
||||
FROM public.likes
|
||||
WHERE user_id = $1 and comment_id = $2;
|
||||
|
||||
6
enshi_back/db/queries/multi_queries.sql
Normal file
6
enshi_back/db/queries/multi_queries.sql
Normal file
@ -0,0 +1,6 @@
|
||||
-- name: GetFavoriteBlogsByUserId :many
|
||||
SELECT sqlc.embed(blogs)
|
||||
FROM favorites
|
||||
JOIN blogs on blogs.blog_id = favorites.blog_id
|
||||
WHERE favorites.user_id = $1;
|
||||
|
||||
15
enshi_back/db/queries/post_tags_queries.sql
Normal file
15
enshi_back/db/queries/post_tags_queries.sql
Normal file
@ -0,0 +1,15 @@
|
||||
-- name: GetAllTagsForPost :many
|
||||
SELECT sqlc.embed(tags)
|
||||
from public.tags tags
|
||||
JOIN public.post_tags post_tags on post_tags.tag_id = tags.tag_id
|
||||
JOIN public.posts posts on posts.post_id = post_tags.post_id;
|
||||
|
||||
-- name: CreatePostTagRelation :one
|
||||
INSERT INTO public.post_tags
|
||||
(post_id, tag_id)
|
||||
VALUES($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeletePostTagRelation :exec
|
||||
DELETE FROM public.post_tags
|
||||
WHERE post_id = $1 AND tag_id = $2;
|
||||
20
enshi_back/db/queries/post_votes_queries.sql
Normal file
20
enshi_back/db/queries/post_votes_queries.sql
Normal file
@ -0,0 +1,20 @@
|
||||
-- name: CreatePostVote :one
|
||||
INSERT INTO public.post_votes
|
||||
(post_id, user_id, vote)
|
||||
VALUES($1, $2, $3)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeletePostVote :exec
|
||||
DELETE FROM public.post_votes
|
||||
WHERE post_id=$1 AND user_id=$2;
|
||||
|
||||
-- name: UpdateVote :one
|
||||
UPDATE public.post_votes
|
||||
SET vote=$1
|
||||
WHERE post_id=$2 AND user_id=$3
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetPostVote :one
|
||||
SELECT vote
|
||||
FROM public.post_votes p_v
|
||||
WHERE p_v.user_id = $1 and p_v.post_id = $2;
|
||||
30
enshi_back/db/queries/posts_queries.sql
Normal file
30
enshi_back/db/queries/posts_queries.sql
Normal file
@ -0,0 +1,30 @@
|
||||
-- name: GetPostsByPostId :one
|
||||
SELECT *
|
||||
FROM public.posts posts
|
||||
where posts.post_id = $1;
|
||||
|
||||
-- name: GetPostsByUserId :many
|
||||
SELECT *
|
||||
FROM public.posts posts
|
||||
where posts.user_id = $1;
|
||||
|
||||
-- name: GetPostsByBlogId :many
|
||||
SELECT *
|
||||
FROM public.posts posts
|
||||
where posts.blog_id = $1;
|
||||
|
||||
-- name: CreatePost :one
|
||||
INSERT INTO public.posts
|
||||
(post_id, blog_id, user_id, title, "content", created_at, updated_at)
|
||||
VALUES($1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdatePostByPostId :one
|
||||
UPDATE public.posts
|
||||
SET blog_id=$1, user_id=$2, title=$3, "content"=$4, updated_at=CURRENT_TIMESTAMP
|
||||
WHERE post_id = $5
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeletePostByPostId :exec
|
||||
DELETE FROM public.posts
|
||||
WHERE post_id=$1;
|
||||
25
enshi_back/db/queries/profiles_queries.sql
Normal file
25
enshi_back/db/queries/profiles_queries.sql
Normal file
@ -0,0 +1,25 @@
|
||||
-- name: CreateProfileForUser :one
|
||||
INSERT INTO public.profiles
|
||||
(user_id, bio, avatar_url, website_url)
|
||||
VALUES($1, '', '', '')
|
||||
RETURNING *;
|
||||
|
||||
-- name: ClearProfileByUserId :one
|
||||
UPDATE public.profiles
|
||||
SET bio='', avatar_url='', website_url=''
|
||||
WHERE user_id=$1
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteProfileByUserId :exec
|
||||
DELETE FROM public.profiles
|
||||
WHERE user_id=$1;
|
||||
|
||||
-- name: GetProfileByUserId :one
|
||||
SELECT * FROM public.profiles WHERE user_id = $1;
|
||||
|
||||
-- name: UpdateProfileByUserId :one
|
||||
UPDATE public.profiles
|
||||
SET bio=$2, avatar_url=$3, website_url=$4
|
||||
WHERE user_id=$1
|
||||
RETURNING *;
|
||||
|
||||
19
enshi_back/db/queries/tags_queries.sql
Normal file
19
enshi_back/db/queries/tags_queries.sql
Normal file
@ -0,0 +1,19 @@
|
||||
-- name: GetTagByTagId :one
|
||||
SELECT tag_id, tag_name
|
||||
FROM public.tags tags
|
||||
where tags.tag_id = $1;
|
||||
|
||||
-- name: CreateTag :one
|
||||
INSERT INTO public.tags
|
||||
(tag_id, tag_name)
|
||||
VALUES($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteTag :exec
|
||||
DELETE FROM public.tags
|
||||
WHERE tag_id = $1;
|
||||
|
||||
-- name: GetAllTags :many
|
||||
SELECT tag_id, tag_name
|
||||
FROM public.tags
|
||||
ORDER BY tag_name ASC;
|
||||
31
enshi_back/db/queries/users_queries.sql
Normal file
31
enshi_back/db/queries/users_queries.sql
Normal file
@ -0,0 +1,31 @@
|
||||
-- name: GetAllUsers :many
|
||||
SELECT * FROM users;
|
||||
|
||||
-- name: GetUserById :one
|
||||
SELECT * FROM users WHERE user_id = $1;
|
||||
|
||||
-- name: GetUserByUsername :one
|
||||
SELECT * FROM users WHERE username = $1;
|
||||
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO public.users
|
||||
(user_id, username, email, "password", created_at, is_admin)
|
||||
VALUES($1, $2, $3, $4, CURRENT_TIMESTAMP, false)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateUserPasswordHash :one
|
||||
UPDATE public.users
|
||||
SET "password"=$1
|
||||
WHERE user_id=$2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteUserById :exec
|
||||
DELETE FROM public.users
|
||||
WHERE user_id=$1;
|
||||
|
||||
-- name: DeleteUserByUsername :exec
|
||||
DELETE FROM public.users
|
||||
WHERE username=$1;
|
||||
|
||||
-- name: GetUserByEmailOrNickname :one
|
||||
SELECT * FROM users WHERE username = $1 OR email = $2 LIMIT 1;
|
||||
24
enshi_back/db/sqlc.yml
Normal file
24
enshi_back/db/sqlc.yml
Normal file
@ -0,0 +1,24 @@
|
||||
version: "2"
|
||||
sql:
|
||||
- engine: "postgresql"
|
||||
schema: "./migrations"
|
||||
queries: "./queries"
|
||||
gen:
|
||||
go:
|
||||
emit_json_tags: true
|
||||
package: "db_repo"
|
||||
out: "./go_queries"
|
||||
sql_package: "pgx/v5"
|
||||
|
||||
overrides:
|
||||
- column: users.password
|
||||
go_struct_tag: validate:"required"
|
||||
- column: users.username
|
||||
go_struct_tag: validate:"required"
|
||||
- column: users.email
|
||||
go_struct_tag: validate:"required,email"
|
||||
|
||||
- db_type: "uuid"
|
||||
go_type:
|
||||
import: 'github.com/google/uuid'
|
||||
type: 'UUID'
|
||||
47
enshi_back/db_connection/databaseSetup.go
Normal file
47
enshi_back/db_connection/databaseSetup.go
Normal file
@ -0,0 +1,47 @@
|
||||
package db_connection
|
||||
|
||||
import (
|
||||
"context"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/env"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// Pgx connection to database
|
||||
var Dbx *pgxpool.Pool
|
||||
var Dbx_connection *pgx.Conn
|
||||
var Sqlc_query = db_repo.New(Dbx)
|
||||
|
||||
func SetupDatabase() error {
|
||||
|
||||
var bd_pass, bd_user string
|
||||
var err error
|
||||
|
||||
if err := env.LookupEnv(&bd_pass, "BD_PASSWORD"); err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return err
|
||||
}
|
||||
if err := env.LookupEnv(&bd_user, "BD_USER"); err != nil {
|
||||
fmt.Printf("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Url to connect
|
||||
url := fmt.Sprintf("postgres://%v:%v@nekiiinkognito.ru:5432/postgres", bd_user, bd_pass)
|
||||
|
||||
// Connecting to database
|
||||
Dbx, err = pgxpool.New(context.Background(), url)
|
||||
Dbx_connection, err = pgx.Connect(context.Background(), url)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Unable to connect")
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("Connected successfully!")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
34
enshi_back/env/env.go
vendored
Normal file
34
enshi_back/env/env.go
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"enshi/auth"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func LookupEnv(dest *string, envVar string) error {
|
||||
if v, exists := os.LookupEnv(envVar); !exists {
|
||||
return fmt.Errorf("%v not found in local env", envVar)
|
||||
} else {
|
||||
*dest = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func LoadEnv(path string) error {
|
||||
|
||||
if err := godotenv.Load(path); err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := LookupEnv(&auth.SecretKey, "SECRET_KEY"); err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Env loaded")
|
||||
return nil
|
||||
}
|
||||
11
enshi_back/global/globalColorsForConsole.go
Normal file
11
enshi_back/global/globalColorsForConsole.go
Normal file
@ -0,0 +1,11 @@
|
||||
package global
|
||||
|
||||
var ResetColor = "\033[0m"
|
||||
var RedColor = "\033[31m"
|
||||
var GreenColor = "\033[32m"
|
||||
var YellowColor = "\033[33m"
|
||||
var BlueColor = "\033[34m"
|
||||
var MagentaColor = "\033[35m"
|
||||
var CyanColor = "\033[36m"
|
||||
var GrayColor = "\033[37m"
|
||||
var WhiteColor = "\033[97m"
|
||||
6
enshi_back/global/globalVars.go
Normal file
6
enshi_back/global/globalVars.go
Normal file
@ -0,0 +1,6 @@
|
||||
package global
|
||||
|
||||
var PathForCookies = "/"
|
||||
var DomainForCookies = "localhost"
|
||||
var SecureForCookies = false
|
||||
var HttpOnlyForCookies = false
|
||||
45
enshi_back/go.mod
Normal file
45
enshi_back/go.mod
Normal file
@ -0,0 +1,45 @@
|
||||
module enshi
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/jackc/pgx/v5 v5.7.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.10.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
100
enshi_back/go.sum
Normal file
100
enshi_back/go.sum
Normal file
@ -0,0 +1,100 @@
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
|
||||
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
158
enshi_back/hasher/passwordHashing.go
Normal file
158
enshi_back/hasher/passwordHashing.go
Normal file
@ -0,0 +1,158 @@
|
||||
package hasher
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
type Argon2Hash struct {
|
||||
time uint32
|
||||
memory uint32
|
||||
threads uint8
|
||||
keyLen uint32
|
||||
saltLen uint32
|
||||
}
|
||||
|
||||
type HashSalt struct {
|
||||
Hash []byte
|
||||
salt []byte
|
||||
StringToStore string
|
||||
}
|
||||
|
||||
// Initializer for algorithm
|
||||
func NewArgon2Hash(time, keyLen, saltLen, memory uint32, threads uint8) *Argon2Hash {
|
||||
return &Argon2Hash{
|
||||
time: time,
|
||||
keyLen: keyLen,
|
||||
saltLen: saltLen,
|
||||
memory: memory,
|
||||
threads: threads,
|
||||
}
|
||||
}
|
||||
|
||||
// Generating salt (byte slice with pseudorandom symbols)
|
||||
func SaltGen(length uint32) ([]byte, error) {
|
||||
salt := make([]byte, length)
|
||||
|
||||
// Read pseudorandom numbers and write them in salt
|
||||
_, err := rand.Read(salt)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return salt, nil
|
||||
|
||||
}
|
||||
|
||||
// Generating hash with given password and salt
|
||||
func (a *Argon2Hash) HashGen(password, salt []byte) (*HashSalt, error) {
|
||||
var err error
|
||||
|
||||
// If salt len is zero => we hashing new password => we need new salt ;)
|
||||
if len(salt) == 0 {
|
||||
salt, err = SaltGen(a.saltLen)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generating password hash with given params
|
||||
hash := argon2.IDKey(password, salt, a.time, a.memory, a.threads, a.keyLen)
|
||||
|
||||
// Salt and hash is byte slice and to properly read them (as string) we need to use this functions
|
||||
saltDecoded := base64.RawStdEncoding.EncodeToString(salt)
|
||||
hashDecoded := base64.RawStdEncoding.EncodeToString(hash)
|
||||
|
||||
// Creating string with salt and password hash
|
||||
stringToStore := fmt.Sprintf("$m=%d,t=%d$%s$%s", a.memory, a.time, saltDecoded, hashDecoded)
|
||||
|
||||
// This is unnecessary structure i created following the guide 0_0
|
||||
return &HashSalt{Hash: hash, salt: salt, StringToStore: stringToStore}, nil
|
||||
|
||||
}
|
||||
|
||||
// Returns error if password hashes are not equal
|
||||
func (a *Argon2Hash) Compare(hash, salt, password []byte) error {
|
||||
|
||||
// We hash given password with salt we created for original one
|
||||
// so we can check wether hashes are equal
|
||||
hashSalt, err := a.HashGen(password, salt)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Comparing hashes
|
||||
if !bytes.Equal(hash, hashSalt.Hash) {
|
||||
return fmt.Errorf("invalid password (hashes does not match)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Function to extract hash and salt from password string
|
||||
// that is stored in db
|
||||
//
|
||||
// returns hash, salt, error
|
||||
func DecodeArgon2String(passwordHash string) ([]byte, []byte, error) {
|
||||
values := strings.Split(passwordHash, "$")
|
||||
|
||||
// Transform string hash in byte slice
|
||||
salt, err := base64.RawStdEncoding.Strict().DecodeString(values[2])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Transform string hash in byte slice
|
||||
hash, err := base64.RawStdEncoding.Strict().DecodeString(values[3])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return hash, salt, nil
|
||||
}
|
||||
|
||||
var Argon2Hasher = NewArgon2Hash(2, 100, 32, 64*1024, 1)
|
||||
|
||||
func Test() {
|
||||
pas := []byte("qwerty1")
|
||||
|
||||
testDbString := "$m=65536,t=2$u0h2MoT48NXJFIRQXW9/i0tNDu427RJv3vIeZQIm8FU$QtUmjmPhsBgWGloNQVRoFkLkHnQwuCqRVfgKA0Sm2QNMPc86vSLxQ/c8JUXroO37qwXdfC9DNvTnm/OOi7GfTBGnJJotLBlonG/9epAMGY453s9UZVeghmvftCagXzPS9QB3cg"
|
||||
|
||||
var salt []byte
|
||||
|
||||
cringe, err := Argon2Hasher.HashGen(pas, salt)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
storedH, storedS, err := DecodeArgon2String(testDbString)
|
||||
if err != nil {
|
||||
fmt.Println("CRINGE", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = Argon2Hasher.Compare(storedH, storedS, []byte("qwerty1"))
|
||||
if err != nil {
|
||||
fmt.Println("DONT MATCH")
|
||||
} else {
|
||||
fmt.Println("MATCH")
|
||||
}
|
||||
|
||||
if err := godotenv.Load("secrets.env"); err != nil {
|
||||
fmt.Println("Error load .env")
|
||||
}
|
||||
|
||||
// fmt.Println(testDbString)
|
||||
fmt.Printf("%s", cringe.StringToStore)
|
||||
fmt.Print("\n\n\n\n")
|
||||
}
|
||||
53
enshi_back/main.go
Normal file
53
enshi_back/main.go
Normal file
@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/env"
|
||||
utils "enshi/utils"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
if err := env.LoadEnv("utils/secret.env"); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := db_connection.SetupDatabase(); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
defer db_connection.Dbx.Close()
|
||||
defer db_connection.Dbx_connection.Close(context.Background())
|
||||
|
||||
router := gin.Default()
|
||||
if err := utils.SetupRotes(router); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Transaction
|
||||
tx, _ := db_connection.Dbx.Begin(context.Background())
|
||||
defer tx.Rollback(context.Background())
|
||||
|
||||
repo := db_repo.New(tx)
|
||||
|
||||
users, _ := repo.GetAllUsers(context.Background())
|
||||
|
||||
for _, user := range users {
|
||||
fmt.Printf("%v\n", user.Username)
|
||||
}
|
||||
|
||||
if err := tx.Commit(context.Background()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
router.Run("localhost:9876")
|
||||
|
||||
fmt.Printf("Hey!, %v", "you")
|
||||
}
|
||||
10
enshi_back/middleware/adminMiddleware.go
Normal file
10
enshi_back/middleware/adminMiddleware.go
Normal file
@ -0,0 +1,10 @@
|
||||
package middleware
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func AdminMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
36
enshi_back/middleware/authMiddleware.go
Normal file
36
enshi_back/middleware/authMiddleware.go
Normal file
@ -0,0 +1,36 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"enshi/auth"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
// token := c.GetHeader("Authorization")
|
||||
|
||||
tokenFromCookies := c.Request.CookiesNamed("auth_cookie")[0].Value
|
||||
cookieClimes, err := auth.ValidateToken(tokenFromCookies)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// claims, err := auth.ValidateToken(token)
|
||||
// if err != nil {
|
||||
// c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()})
|
||||
// c.Abort()
|
||||
// return
|
||||
// }
|
||||
|
||||
// Claims -> data stored in token
|
||||
c.Set(ContextUserId, cookieClimes["id"])
|
||||
c.Set(ContextTokenData, cookieClimes)
|
||||
c.Next()
|
||||
|
||||
}
|
||||
}
|
||||
25
enshi_back/middleware/corsMiddleware.go
Normal file
25
enshi_back/middleware/corsMiddleware.go
Normal file
@ -0,0 +1,25 @@
|
||||
package middleware
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, "+
|
||||
"authorization, Authorization, accept, origin, Cache-Control, "+
|
||||
"X-Requested-With, Cookie",
|
||||
)
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
||||
c.Writer.Header().Set("Access-Control-Expose-Headers", "Access-Token, Uid, Authorization")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
44
enshi_back/middleware/getters/claims.go
Normal file
44
enshi_back/middleware/getters/claims.go
Normal file
@ -0,0 +1,44 @@
|
||||
package getters
|
||||
|
||||
import (
|
||||
"enshi/auth"
|
||||
"enshi/global"
|
||||
"enshi/middleware"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
func GetClaimsFromContext(c *gin.Context) (auth.UserInfoJWT, error) {
|
||||
var UserInfo auth.UserInfoJWT
|
||||
|
||||
claims, exists := c.Get(middleware.ContextTokenData)
|
||||
|
||||
if !exists {
|
||||
return auth.UserInfoJWT{}, fmt.Errorf("error getting user id")
|
||||
}
|
||||
|
||||
parsedUserId, err := strconv.ParseInt(
|
||||
claims.(jwt.MapClaims)["id"].(string),
|
||||
10,
|
||||
64,
|
||||
)
|
||||
if err != nil {
|
||||
return auth.UserInfoJWT{}, fmt.Errorf("error parsing user id")
|
||||
}
|
||||
|
||||
UserInfo.Id = parsedUserId
|
||||
UserInfo.Username = claims.(jwt.MapClaims)["username"].(string)
|
||||
isAdmin, err := strconv.ParseBool(claims.(jwt.MapClaims)["isAdmin"].(string))
|
||||
if err != nil {
|
||||
UserInfo.IsAdmin = false
|
||||
fmt.Println(global.RedColor + "isAdmin prop corrupted" + global.ResetColor)
|
||||
} else {
|
||||
UserInfo.IsAdmin = isAdmin
|
||||
}
|
||||
|
||||
return UserInfo, nil
|
||||
|
||||
}
|
||||
23
enshi_back/middleware/getters/userId.go
Normal file
23
enshi_back/middleware/getters/userId.go
Normal file
@ -0,0 +1,23 @@
|
||||
package getters
|
||||
|
||||
import (
|
||||
"enshi/middleware"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func GetUserIdFromContext(c *gin.Context) (int64, error) {
|
||||
userId, exists := c.Get(middleware.ContextUserId)
|
||||
|
||||
if !exists {
|
||||
return -1, fmt.Errorf("error getting user id")
|
||||
}
|
||||
|
||||
if parsedUserId, err := strconv.ParseInt(userId.(string), 10, 64); err != nil {
|
||||
return -1, fmt.Errorf("error parsing user id")
|
||||
} else {
|
||||
return parsedUserId, nil
|
||||
}
|
||||
}
|
||||
5
enshi_back/middleware/keys.go
Normal file
5
enshi_back/middleware/keys.go
Normal file
@ -0,0 +1,5 @@
|
||||
package middleware
|
||||
|
||||
var ContextUserId = "id"
|
||||
var ContextIsAdmin = "isAdmin"
|
||||
var ContextTokenData = "tokenData"
|
||||
17
enshi_back/routes/changeUserProfile.go
Normal file
17
enshi_back/routes/changeUserProfile.go
Normal file
@ -0,0 +1,17 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func ChangeUserProfile(c *gin.Context) {
|
||||
var userProfileParams db_repo.UpdateProfileByUserIdParams
|
||||
|
||||
if err := c.BindJSON(&userProfileParams); err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
}
|
||||
|
||||
}
|
||||
44
enshi_back/routes/createPost.go
Normal file
44
enshi_back/routes/createPost.go
Normal file
@ -0,0 +1,44 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func CreatePost(c *gin.Context) {
|
||||
var postParams db_repo.CreatePostParams
|
||||
|
||||
if err := c.BindJSON(&postParams); err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userId, err := getters.GetUserIdFromContext(c)
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
postParams.UserID = userId
|
||||
|
||||
if uuidForPost, err := uuid.NewV7(); err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
} else {
|
||||
postParams.PostID = -int64(binary.BigEndian.Uint64(uuidForPost[8:]))
|
||||
}
|
||||
|
||||
query := db_repo.New(db_connection.Dbx)
|
||||
if _, err := query.CreatePost(context.Background(), postParams); err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
rest_api_stuff.OkAnswer(c, "Post has been created!")
|
||||
}
|
||||
51
enshi_back/routes/deletePost.go
Normal file
51
enshi_back/routes/deletePost.go
Normal file
@ -0,0 +1,51 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/middleware/getters"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func DeletePost(c *gin.Context) {
|
||||
var deletePostId struct {
|
||||
postId int64
|
||||
}
|
||||
|
||||
if err := c.BindJSON(&deletePostId); err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userClaims, err := getters.GetClaimsFromContext(c)
|
||||
if err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
query := db_repo.New(db_connection.Dbx)
|
||||
post, err := query.GetPostsByPostId(context.Background(), deletePostId.postId)
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if post.UserID != userClaims.Id {
|
||||
rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you are not the author"))
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Add block of code, so admin could delete anything
|
||||
|
||||
err = query.DeletePostByPostId(context.Background(), deletePostId.postId)
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
rest_api_stuff.OkAnswer(c, "post has been deleted")
|
||||
}
|
||||
73
enshi_back/routes/login.go
Normal file
73
enshi_back/routes/login.go
Normal file
@ -0,0 +1,73 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"enshi/auth"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/global"
|
||||
"enshi/hasher"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Login(c *gin.Context) {
|
||||
type content struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
var body content
|
||||
|
||||
err := c.BindJSON(&body)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusBadRequest, gin.H{"error 1st": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
repo := db_repo.New(db_connection.Dbx)
|
||||
user, err := repo.GetUserByUsername(context.Background(), body.Username)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
password_hash, salt, err := hasher.DecodeArgon2String(user.Password)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
err = hasher.Argon2Hasher.Compare(password_hash, salt, []byte(body.Password))
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := auth.UserInfoJWT{
|
||||
Id: user.UserID,
|
||||
Username: user.Username,
|
||||
IsAdmin: user.IsAdmin,
|
||||
}
|
||||
|
||||
token, err := auth.CreateToken(userInfo)
|
||||
if err != nil {
|
||||
c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
cookieName := "auth_cookie"
|
||||
cookieValue := token
|
||||
maxAge := int(2 * time.Hour.Seconds()) // Cookie expiry time in seconds (1 hour)
|
||||
path := global.PathForCookies // Cookie path
|
||||
domain := global.DomainForCookies // Set domain (localhost for testing)
|
||||
secure := global.SecureForCookies // Secure cookie (set to true in production with HTTPS)
|
||||
httpOnly := global.HttpOnlyForCookies // HTTP only, so it can't be accessed by JavaScript
|
||||
|
||||
c.Header("Authorization", token)
|
||||
c.SetCookie(cookieName, cookieValue, maxAge, path, domain, secure, httpOnly)
|
||||
c.IndentedJSON(http.StatusOK, gin.H{"token": token})
|
||||
|
||||
}
|
||||
122
enshi_back/routes/registerUser.go
Normal file
122
enshi_back/routes/registerUser.go
Normal file
@ -0,0 +1,122 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
rest_api_stuff "enshi/REST_API_stuff"
|
||||
"enshi/auth"
|
||||
db_repo "enshi/db/go_queries"
|
||||
"enshi/db_connection"
|
||||
"enshi/global"
|
||||
"enshi/hasher"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func RegisterUser(c *gin.Context) {
|
||||
var userParams db_repo.CreateUserParams
|
||||
|
||||
if err := c.BindJSON(&userParams); err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
if err := validate.Struct(userParams); err != nil {
|
||||
rest_api_stuff.BadRequestAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
query := db_repo.New(db_connection.Dbx)
|
||||
sameNicknameOrEmailUser, _ := query.GetUserByEmailOrNickname(
|
||||
context.Background(),
|
||||
db_repo.GetUserByEmailOrNicknameParams{
|
||||
Username: userParams.Username,
|
||||
Email: userParams.Email,
|
||||
},
|
||||
)
|
||||
if sameNicknameOrEmailUser.Username == userParams.Username {
|
||||
rest_api_stuff.ConflictAnswer(
|
||||
c,
|
||||
fmt.Errorf("username"),
|
||||
)
|
||||
return
|
||||
} else if sameNicknameOrEmailUser.Email == userParams.Email {
|
||||
rest_api_stuff.ConflictAnswer(
|
||||
c,
|
||||
fmt.Errorf("email"),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
transaction, err := db_connection.Dbx.Begin(context.Background())
|
||||
defer transaction.Rollback(context.Background())
|
||||
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
query_transaction := query.WithTx(transaction)
|
||||
|
||||
passwordHashSalt, err := hasher.Argon2Hasher.HashGen([]byte(userParams.Password), []byte{})
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userParams.Password = passwordHashSalt.StringToStore
|
||||
|
||||
uuid, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
userParams.UserID = -int64(
|
||||
binary.BigEndian.Uint64(uuid[8:]),
|
||||
)
|
||||
|
||||
if _, err := query_transaction.CreateUser(context.Background(), userParams); err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := query_transaction.CreateProfileForUser(
|
||||
context.Background(),
|
||||
userParams.UserID,
|
||||
); err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
tokenParams := auth.UserInfoJWT{
|
||||
Id: userParams.UserID,
|
||||
Username: userParams.Username,
|
||||
IsAdmin: false,
|
||||
}
|
||||
|
||||
token, err := auth.CreateToken(tokenParams)
|
||||
if err != nil {
|
||||
rest_api_stuff.InternalErrorAnswer(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
cookieParams := &rest_api_stuff.CookieParams{
|
||||
Name: "auth_cookie",
|
||||
Value: token,
|
||||
MaxAge: int(2 * time.Hour.Seconds()),
|
||||
Path: global.PathForCookies,
|
||||
Domain: global.DomainForCookies,
|
||||
Secure: global.SecureForCookies,
|
||||
HttpOnly: global.HttpOnlyForCookies,
|
||||
}
|
||||
|
||||
transaction.Commit(context.Background())
|
||||
rest_api_stuff.SetCookie(c, cookieParams)
|
||||
rest_api_stuff.OkAnswer(c, "User has been created!")
|
||||
}
|
||||
40
enshi_back/utils/routesSetup.go
Normal file
40
enshi_back/utils/routesSetup.go
Normal file
@ -0,0 +1,40 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"enshi/middleware"
|
||||
"enshi/routes"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func testCookie(c *gin.Context) {
|
||||
cock, _ := c.Cookie("auth_cookie")
|
||||
c.IndentedJSON(http.StatusOK, gin.H{"token": "SLESAR' U STASA " + strings.Split(cock, "_")[0]})
|
||||
}
|
||||
|
||||
func SetupRotes(g *gin.Engine) error {
|
||||
g.Use(middleware.CORSMiddleware())
|
||||
|
||||
// Free group routes
|
||||
freeGroup := g.Group("/")
|
||||
|
||||
freeGroup.GET("getCookie", testCookie)
|
||||
|
||||
freeGroup.POST("login", routes.Login)
|
||||
freeGroup.POST("registerUser", routes.RegisterUser)
|
||||
|
||||
// Auth group routes
|
||||
authGroup := g.Group("/")
|
||||
authGroup.Use(middleware.AuthMiddleware())
|
||||
authGroup.POST("createPost", routes.CreatePost)
|
||||
authGroup.POST("deletePost", routes.DeletePost)
|
||||
|
||||
adminGroup := authGroup.Group("/admin/")
|
||||
adminGroup.Use(middleware.AdminMiddleware())
|
||||
|
||||
authGroup.POST("changeUserProfile", routes.ChangeUserProfile)
|
||||
|
||||
return nil
|
||||
}
|
||||
171
package-lock.json
generated
Normal file
171
package-lock.json
generated
Normal file
@ -0,0 +1,171 @@
|
||||
{
|
||||
"name": "Enshi",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.59.0",
|
||||
"axios": "^1.7.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.0.tgz",
|
||||
"integrity": "sha512-WGD8uIhX6/deH/tkZqPNcRyAhDUqs729bWKoByYHSogcshXfFbppOdTER5+qY7mFvu8KEFJwT0nxr8RfPTVh0Q==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.59.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.0.tgz",
|
||||
"integrity": "sha512-YDXp3OORbYR+8HNQx+lf4F73NoiCmCcSvZvgxE29OifmQFk0sBlO26NWLHpcNERo92tVk3w+JQ53/vkcRUY1hA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.59.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
package.json
Normal file
6
package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^5.59.0",
|
||||
"axios": "^1.7.7"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user