Compare commits

..

2 Commits

Author SHA1 Message Date
5caaa2bf5e API: docs updated 2024-05-16 18:06:53 +02:00
c6b3efad4e Added: note in backend 2024-05-16 18:05:05 +02:00
14 changed files with 133 additions and 169 deletions

View File

@ -1,32 +0,0 @@
name: Build DeguApp backend
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set up Node.js and TypeScript
uses: actions/setup-node@v1
with:
node-version: "20.14.0"
- name: npm install
working-directory: api/
run: |
npm install
- name: npm run build
working-directory: api/
run: |
npm run build --if-present
- name: npm run test
working-directory: api/
run: |
npm run test

View File

@ -35,12 +35,7 @@ To get started with DeguApp, follow these steps:
2. Install dependencies: 2. Install dependencies:
```bash ```bash
# frontend cd deguapp
cd deguapp/frontend
npm install
# backend
cd deguapp/api
npm install npm install
``` ```
@ -48,6 +43,11 @@ To get started with DeguApp, follow these steps:
5. Open the app in your browser or Android emulator and start exploring! 5. Open the app in your browser or Android emulator and start exploring!
## Contributing
Contributions are welcome! If you'd like to contribute to DeguApp, please fork the repository and submit a pull request with your changes.
Use the upstream of the project, which can be found at https:/git.filiprojek.cz/fr/deguapp. **GitHub repository is just a mirror!**
## Local builds ## Local builds
### Android ### Android
@ -74,24 +74,12 @@ bundletool build-apks --bundle=./frontend/android/app/build/outputs/bundle/relea
bundletool install-apks --apks=./deguapp.apks bundletool install-apks --apks=./deguapp.apks
``` ```
#### Resources:
### Resources:
- https://github.com/expo/eas-cli/issues/1300 - https://github.com/expo/eas-cli/issues/1300
- https://reactnative.dev/docs/signed-apk-android#generating-the-release-aab - https://reactnative.dev/docs/signed-apk-android#generating-the-release-aab
### Server
```bash
cd api/
npm i
npm run build
```
## Contributing
Contributions are welcome! If you'd like to contribute to DeguApp, please fork the repository and submit a pull request with your changes.
Use the upstream of the project, which can be found at https:/git.filiprojek.cz/fr/deguapp. **GitHub repository is just a mirror!**
## License ## License
This project is licensed under the GNU GPLv3 License - see the [LICENSE](LICENSE) file for details. This project is licensed under the GNU GPLv3 License - see the [LICENSE](LICENSE) file for details.

View File

@ -28,6 +28,11 @@ const schema = new Schema<IReview | any>(
type: Boolean, type: Boolean,
required: true, required: true,
}, },
note: {
type: String,
required: false,
default: ""
},
beer_id: { beer_id: {
type: String, type: String,
required: true, required: true,

View File

@ -1 +1 @@
{"version":"2.0.0","endpoints":{"user":{"signup":{"name":"user","operation":"signup","route":"/api/v1/auth/signup","method":"POST","description":"user signup api","body":{"username":"testuser","email":"text@example.com","password":"Test1234"},"response":"status object"},"signin":{"name":"user","operation":"signin","route":"/api/v1/auth/signin","method":"POST","description":"user signin api","body":{"email":"text@example.com","password":"Test1234"},"response":"status object"},"logout":{"name":"user","operation":"logout","route":"/api/v1/auth/logout","method":"POST","description":"user logout api","body":{},"response":"status object"},"status":{"name":"user","operation":"status","route":"/api/v1/auth/status","method":"GET","description":"user login status api","response":"status code | user object"}},"beer":{"add":{"name":"beer","operation":"add","route":"/api/v1/beer/add","method":"POST","description":"beer add api","body":{"brand":"Pilsner Urqell","name":"Kozel","degree":11,"packaging":"can","photos":"optional field | max 4 images | formData"},"response":"status object | beer object"},"get":{"name":"beer","operation":"get","route":"/api/v1/beer/get","method":"GET","description":"beer get api","response":"status object | array of beer objects"},"del":{"name":"beer","operation":"del","route":"/api/v1/beer/del","method":"POST","description":"beer del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"},"edit":{"name":"beer","operation":"edit","route":"/api/v1/beer/edit","method":"POST","description":"beer edit api","body":{"_id":"6355b95dc03fad77bc380146","brand":"Pilsner Urqell","name":"Radegast","degree":12,"packaging":"bottle","imgs":[],"photos":"optional field | max 4 images | formData"},"response":"status object | beer data"}},"docs":{"get_all":{"name":"docs","operation":"get_all","route":"/api/v1","method":"GET","description":"Get docs json","response":"docs json"}},"review":{"add":{"name":"review","operation":"add","route":"/api/v1/review/add","method":"POST","description":"review add api","body":{"beer_id":"6352b303b71cb62222f39895","foam":3,"bitter_sweetness":2,"taste":5,"packaging":3,"sourness":false,"would_again":true},"response":"status object | review object"},"get":{"name":"review","operation":"get","route":"/api/v1/review/get","method":"GET","description":"review get api","response":"status object | array of review objects"},"del":{"name":"review","operation":"del","route":"/api/v1/review/del","method":"POST","description":"review del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"}}}} {"version":"2.0.0","endpoints":{"user":{"signup":{"name":"user","operation":"signup","route":"/api/v1/auth/signup","method":"POST","description":"user signup api","body":{"username":"testuser","email":"text@example.com","password":"Test1234"},"response":"status object"},"signin":{"name":"user","operation":"signin","route":"/api/v1/auth/signin","method":"POST","description":"user signin api","body":{"email":"text@example.com","password":"Test1234"},"response":"status object"},"logout":{"name":"user","operation":"logout","route":"/api/v1/auth/logout","method":"POST","description":"user logout api","body":{},"response":"status object"},"status":{"name":"user","operation":"status","route":"/api/v1/auth/status","method":"GET","description":"user login status api","response":"status code | user object"}},"beer":{"add":{"name":"beer","operation":"add","route":"/api/v1/beer/add","method":"POST","description":"beer add api","body":{"brand":"Pilsner Urqell","name":"Kozel","degree":11,"packaging":"can","photos":"optional field | max 4 images | formData"},"response":"status object | beer object"},"get":{"name":"beer","operation":"get","route":"/api/v1/beer/get","method":"GET","description":"beer get api","response":"status object | array of beer objects"},"del":{"name":"beer","operation":"del","route":"/api/v1/beer/del","method":"POST","description":"beer del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"},"edit":{"name":"beer","operation":"edit","route":"/api/v1/beer/edit","method":"POST","description":"beer edit api","body":{"_id":"6355b95dc03fad77bc380146","brand":"Pilsner Urqell","name":"Radegast","degree":12,"packaging":"bottle","imgs":[],"photos":"optional field | max 4 images | formData"},"response":"status object | beer data"}},"docs":{"get_all":{"name":"docs","operation":"get_all","route":"/api/v1","method":"GET","description":"Get docs json","response":"docs json"}},"review":{"add":{"name":"review","operation":"add","route":"/api/v1/review/add","method":"POST","description":"review add api","body":{"beer_id":"6352b303b71cb62222f39895","foam":3,"bitter_sweetness":2,"taste":5,"packaging":3,"sourness":false,"would_again":true,"note":"Pretty good beer"},"response":"status object | review object"},"get":{"name":"review","operation":"get","route":"/api/v1/review/get","method":"GET","description":"review get api","response":"status object | array of review objects"},"del":{"name":"review","operation":"del","route":"/api/v1/review/del","method":"POST","description":"review del api","body":{"_id":"6352b303b71cb62222f39895"},"response":"status object"}}}}

View File

@ -20,7 +20,8 @@ export const add = yup.object({
packaging: yup.number().min(1).max(5).required(), packaging: yup.number().min(1).max(5).required(),
sourness: yup.boolean().required(), sourness: yup.boolean().required(),
would_again: yup.boolean().required(), would_again: yup.boolean().required(),
user_id: yup.string().notRequired() user_id: yup.string().notRequired(),
note: yup.string().notRequired()
}); });
export interface IReview extends yup.InferType<typeof add>, mongooseAddition {} export interface IReview extends yup.InferType<typeof add>, mongooseAddition {}
export const addExam: IReview = { export const addExam: IReview = {
@ -31,6 +32,7 @@ export const addExam: IReview = {
packaging: 3, packaging: 3,
sourness: false, sourness: false,
would_again: true, would_again: true,
note: "Pretty good beer"
}; };
// Remove // Remove

View File

@ -73,6 +73,15 @@ describe("POST /api/v1/review/add", () => {
expect(res.statusCode).toBe(400); expect(res.statusCode).toBe(400);
}); });
test("should drop 201 (missing note)", async () => {
const jwt = await login();
const body: any = { ...addExam };
delete body.note;
const res = await request.post(url).set("Cookie", jwt).send(body);
expect(res.statusCode).toBe(201);
})
test("should drop 201", async () => { test("should drop 201", async () => {
const jwt = await login(); const jwt = await login();
const res = await request.post(url).set("Cookie", jwt).send(addExam); const res = await request.post(url).set("Cookie", jwt).send(addExam);

View File

@ -12,7 +12,9 @@
"resizeMode": "contain", "resizeMode": "contain",
"backgroundColor": "#ffffff" "backgroundColor": "#ffffff"
}, },
"assetBundlePatterns": ["**/*"], "assetBundlePatterns": [
"**/*"
],
"ios": { "ios": {
"supportsTablet": true "supportsTablet": true
}, },

View File

@ -18,7 +18,7 @@ export default function BeerAdd() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [items, setItems] = useState([ const [items, setItems] = useState([
{ label: "Tank beer", value: "tank" }, { label: "Tank beer", value: "tank" },
{ label: "Keg beer", value: "keg" }, { label: "Cask beer", value: "cask" },
{ label: "Glass bottle", value: "glass" }, { label: "Glass bottle", value: "glass" },
{ label: "Can", value: "can" }, { label: "Can", value: "can" },
{ label: "PET bottle", value: "pet" }, { label: "PET bottle", value: "pet" },

View File

@ -19,7 +19,7 @@ export default function reviewAdd() {
const [openSourness, setOpenSourness] = useState(false); const [openSourness, setOpenSourness] = useState(false);
const [openAgain, setOpenAgain] = useState(false); const [openAgain, setOpenAgain] = useState(false);
// foam // pěna
const [itemFoam, setFoamValue] = useState(null); const [itemFoam, setFoamValue] = useState(null);
const [foam, setFoam] = useState([ const [foam, setFoam] = useState([
{ {
@ -54,11 +54,11 @@ export default function reviewAdd() {
}, },
]); ]);
// bitter / sweetness // hořkost / sladkost
const [itemBitter_sweetness, setBitter_sweetnessValue] = useState(null); const [itemBitter_sweetness, setBitter_sweetnessValue] = useState(null);
const [bitter_sweetness, setBitter_sweetness] = useState([ const [bitter_sweetness, setBitter_sweetness] = useState([
{ {
label: "Bitter", label: "Bad",
value: "1", value: "1",
icon: () => ( icon: () => (
<Image <Image
@ -78,7 +78,7 @@ export default function reviewAdd() {
), ),
}, },
{ {
label: "Sweet", label: "Excellent",
value: "3", value: "3",
icon: () => ( icon: () => (
<Image <Image
@ -89,7 +89,7 @@ export default function reviewAdd() {
}, },
]); ]);
// taste //chuť
const [itemTaste, setTasteValue] = useState(null); const [itemTaste, setTasteValue] = useState(null);
const [taste, setTaste] = useState([ const [taste, setTaste] = useState([
{ {
@ -199,7 +199,7 @@ export default function reviewAdd() {
}, },
]); ]);
// sourness //kyselost
const [itemSourness, setSournessValue] = useState(null); const [itemSourness, setSournessValue] = useState(null);
const [sourness, setSourness] = useState([ const [sourness, setSourness] = useState([
{ {
@ -224,7 +224,7 @@ export default function reviewAdd() {
}, },
]); ]);
// would again //dal bych si znovu?
const [itemAgain, setAgainValue] = useState(null); const [itemAgain, setAgainValue] = useState(null);
const [again, setAgain] = useState([ const [again, setAgain] = useState([
{ {

View File

@ -90,7 +90,7 @@ export function AuthProvider({ children }) {
}); });
if (resUser.status != 200) { if (resUser.status != 200) {
throw Error("Username or password is incorrect!"); throw Error("user does not have user data");
} }
const userData = await resUser.json(); const userData = await resUser.json();
@ -104,7 +104,7 @@ export function AuthProvider({ children }) {
await storageUtil.setItem(TOKEN_KEY, loginData.data.jwt); await storageUtil.setItem(TOKEN_KEY, loginData.data.jwt);
} catch (err) { } catch (err) {
console.error("Failed to log in", err); console.error("Failed to log in", err);
return { error: true, msg: err }; return { error: true, msg: err.res };
} }
} }

View File

@ -20,12 +20,8 @@ function LoginPage() {
} }
}, [authState.authenticated]); }, [authState.authenticated]);
async function login() { function login() {
const res = await onLogin(email, pass); onLogin(email, pass);
if (res !== undefined && res.error === true) {
alert(res.msg);
return;
}
} }
return ( return (

View File

@ -16,28 +16,23 @@ function SignupPage() {
const { onSignin } = useAuth(); const { onSignin } = useAuth();
async function signin() { async function signin() {
if (pass1 != pass2) { if (pass1 == pass2) {
alert("Passwords are not same!");
return;
}
const res = await onSignin(username, email, pass1); const res = await onSignin(username, email, pass1);
const data = await res.json(); if (res.error) {
if (res.msg.message == "validation error") {
if (res.status == 400) { alert(res.msg.data.message);
if (data.message == "validation error") {
alert(data.data.message);
} else { } else {
alert("Something went wrong"); alert(res.msg.message);
} }
return;
} }
if (!res.error) {
if (res.status == 201) {
alert("You have been successfully registered. Please Log In"); alert("You have been successfully registered. Please Log In");
router.replace("/login"); router.replace("/login");
}
return; return;
} }
alert("Passwords are not same!");
} }
return ( return (

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json", "$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
"files": { "files": {
"ignore": [".expo/", ".vscode/", "node_modules/", "dist/"] "ignore": [".expo/", ".vscode/", "node_modules/"]
}, },
"organizeImports": { "organizeImports": {
"enabled": true "enabled": true

View File

@ -7,8 +7,7 @@
"android": "expo start --android", "android": "expo start --android",
"ios": "expo start --ios", "ios": "expo start --ios",
"web": "expo start --web", "web": "expo start --web",
"build:web": "npx expo export", "build:web": "npx expo export"
"format": "npx @biomejs/biome format --write ."
}, },
"dependencies": { "dependencies": {
"@expo/metro-runtime": "~3.2.1", "@expo/metro-runtime": "~3.2.1",