Skip to content

Commit

Permalink
Merge pull request #85 from kronos-et-al/backend
Browse files Browse the repository at this point in the history
Backend
  • Loading branch information
worldofjoni authored Aug 14, 2023
2 parents 531727e + 817f682 commit 97265ff
Show file tree
Hide file tree
Showing 23 changed files with 206 additions and 101 deletions.
26 changes: 10 additions & 16 deletions .github/workflows/deploy_backend.yaml
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
#
name: Publish backend to GHCR

# Configures this workflow to run every time a change is pushed to the branch called `release`.
on:
workflow_dispatch:
release:
types: [published]
push:
paths: ["backend/**"]


# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds.
env:
REGISTRY: ghcr.io
IMAGE_NAME: mensa-app
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
IMAGE_NAME: kronos-et-al/mensa-app
jobs:
build-and-push-backend:
runs-on: ubuntu-latest
Expand All @@ -23,30 +21,26 @@ jobs:
permissions:
contents: read
packages: write
#

steps:
- name: Checkout repository
uses: actions/checkout@v3
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
- name: Log in to the Container registry
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
- name: Build and push Docker image
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
uses: docker/build-push-action@v4
with:
context: .
context: "{{defaultContext}}:backend"
file: Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 🥘 MensaApp

<!--- [![Build-Android](https://github.com/kronos-et-al/MensaApp/actions/workflows/main.yml/badge.svg)](https://github.com/kronos-et-al/MensaApp/actions/workflows/main.yml)-->
[![Build-Rust](https://github.com/kronos-et-al/MensaApp/actions/workflows/rust.yml/badge.svg)](https://github.com/kronos-et-al/MensaApp/actions/workflows/rust.yml) [![codecov](https://codecov.io/gh/kronos-et-al/MensaApp/branch/main/graph/badge.svg?token=2CZXSPAP48)](https://codecov.io/gh/kronos-et-al/MensaApp) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kronos-et-al/MensaApp/blob/main/LICENSE) [![version](https://shields.io/github/v/release/kronos-et-al/MensaApp)](https://github.com/kronos-et-al/MensaApp/releases) ![GitHub Repo stars](https://img.shields.io/github/stars/kronos-et-al/MensaApp)
[![Build-Rust](https://github.com/kronos-et-al/MensaApp/actions/workflows/rust.yml/badge.svg)](https://github.com/kronos-et-al/MensaApp/actions/workflows/rust.yml) [![Docker](https://ghcr-badge.egpl.dev/kronos-et-al/mensa-app/size?color=%2344cc11&tag=latest&label=docker+image+size&trim=)](https://github.com/kronos-et-al/MensaApp/pkgs/container/mensa-app) [![codecov](https://codecov.io/gh/kronos-et-al/MensaApp/branch/main/graph/badge.svg?token=2CZXSPAP48)](https://codecov.io/gh/kronos-et-al/MensaApp) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kronos-et-al/MensaApp/blob/main/LICENSE) [![version](https://shields.io/github/v/release/kronos-et-al/MensaApp)](https://github.com/kronos-et-al/MensaApp/releases) ![GitHub Repo stars](https://img.shields.io/github/stars/kronos-et-al/MensaApp)


**Application for communication and collective exchange of
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

This file was deleted.

6 changes: 5 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ COPY . .
ENV SQLX_OFFLINE=true
RUN cargo install --path . --profile release

# actual running image
FROM debian:bullseye-slim
# RUN apt-get update && apt-get install -y extra-runtime-dependencies && rm -rf /var/lib/apt/lists/*

# install http dependencies (through curl)
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

COPY --from=builder /usr/local/cargo/bin/mensa-app-backend /usr/local/bin/mensa-app-backend
EXPOSE 80/tcp

Expand Down
2 changes: 1 addition & 1 deletion backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The docker container can be run using
```
docker run \
-p 80:80
-e DATABASE_URL="postgres://<db user>:<db password>@<db host>:<db port>/<db name>"
-e DATABASE_URL=postgres://<db user>:<db password>@<db host>:<db port>/<db name>
-e SMTP_SERVER=<domain of mail server> \
-e SMTP_PORT=<port of mail server> \
-e SMTP_USERNAME=<username of mail server> \
Expand Down
45 changes: 45 additions & 0 deletions backend/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
services:
mensa-app:
image: ghcr.io/kronos-et-al/mensa-app
#command: --migrate # only run for the first time to create initial database structure
ports:
- 80:80
environment: # please configure accordingly <------------------------------------------------------------
- DATABASE_URL=postgres://postgres:secret_password@database/mensa_app # only change password if wanted
- SMTP_SERVER=<domain of mail server>
- SMTP_PORT=<port of mail server>
- SMTP_USERNAME=<username of mail server>
- SMTP_PASSWORD=<password of mail server>
- ADMIN_EMAIL=<email address admin notofocations should be send to>
- FLICKR_API_KEY=<flickr public api key>
- TZ=Europe/Berlin
restart: unless-stopped
depends_on:
- database
database:
image: postgres
volumes:
- dbdata:/var/lib/postgresql/data
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret_password # change here and above <------------------------------------------
- POSTGRES_DB=mensa_app
- TZ=Europe/Berlin
restart: unless-stopped
admin:
image: dpage/pgadmin4
environment: # change <----------------------------------------------------------------------------------
- PGADMIN_DEFAULT_EMAIL=
- PGADMIN_DEFAULT_PASSWORD=
- TZ=Europe/Berlin
ports:
- 8888:80
volumes:
- pgadmin:/var/lib/pgadmin
restart: unless-stopped
depends_on:
- database

volumes:
dbdata:
pgadmin:
2 changes: 1 addition & 1 deletion backend/migrations/20230703130648_food_additive.up.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- Add up migration script here
CREATE TYPE additive AS ENUM ('COLORANT','PRESERVING_AGENTS','ANTIOXIDANT_AGENTS','FLAVOUR_ENHANCER','PHOSPHATE','SURFACE_WAXED','SULPHUR','ARTIFICALLY_BLACKENED_OLIVES','SWEETENER','LAXATIVE_IF_OVERUSED','PHENYLALANINE','ALCOHOL','PRESSED_MEAT','GLAZING_WITH_CACAO','PRESSED_FISH');
CREATE TYPE additive AS ENUM ('COLORANT','PRESERVING_AGENTS','ANTIOXIDANT_AGENTS','FLAVOUR_ENHANCER','PHOSPHATE','SURFACE_WAXED','SULPHUR','ARTIFICIALLY_BLACKENED_OLIVES','SWEETENER','LAXATIVE_IF_OVERUSED','PHENYLALANINE','ALCOHOL','PRESSED_MEAT','GLAZING_WITH_CACAO','PRESSED_FISH');

CREATE TABLE food_additive (
food_id uuid REFERENCES food(food_id),
Expand Down
8 changes: 7 additions & 1 deletion backend/src/interface/image_hoster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub trait ImageHoster: Send + Sync {
/// Checks if an image still exists at the hoster website.
async fn check_existence(&self, image_id: &str) -> Result<bool>;
/// Checks whether the licence is acceptable for our purposes.
async fn check_licence(&self, image_id: &str) -> Result<bool>;
async fn check_licence(&self, image_id: &str) -> Result<()>;
}

/// Enum describing the possible ways, a image hoster request can fail.
Expand All @@ -24,6 +24,12 @@ pub enum ImageHosterError {
/// Photo not found error
#[error("the photo id passed was not a valid photo id")]
PhotoNotFound,
/// License invalid. List of valid licenses:
/// - `No known copyright restrictions`
/// - `Public Domain Dedication (CC0)`
/// - `Public Domain Mark`
#[error("invalid license '{0}' detected, expected one of: {1}")]
InvalidLicense(String, String),
/// Permission denied error
#[error("the calling user does not have permission to view the photo")]
PermissionDenied,
Expand Down
49 changes: 45 additions & 4 deletions backend/src/layer/data/database/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,12 @@ impl CommandDataAccess for PersistentCommandData {

async fn add_upvote(&self, image_id: Uuid, user_id: Uuid) -> Result<()> {
sqlx::query!(
"INSERT INTO image_rating (user_id, image_id, rating) VALUES ($1, $2, 1)",
"
INSERT INTO image_rating (user_id, image_id, rating)
VALUES ($1, $2, 1)
ON CONFLICT (user_id, image_id)
DO UPDATE SET rating = 1
",
user_id,
image_id
)
Expand All @@ -85,7 +90,12 @@ impl CommandDataAccess for PersistentCommandData {

async fn add_downvote(&self, image_id: Uuid, user_id: Uuid) -> Result<()> {
sqlx::query!(
"INSERT INTO image_rating (user_id, image_id, rating) VALUES ($1, $2, -1)",
"
INSERT INTO image_rating (user_id, image_id, rating)
VALUES ($1, $2, -1)
ON CONFLICT (user_id, image_id)
DO UPDATE SET rating = -1
",
user_id,
image_id
)
Expand Down Expand Up @@ -260,7 +270,7 @@ mod test {
let upvotes = number_of_votes(&pool, 1).await;
assert!(command.add_upvote(image_id, user_id).await.is_ok());
assert_eq!(number_of_votes(&pool, 1).await, upvotes + 1);
assert!(command.add_upvote(image_id, user_id).await.is_err());
assert!(command.add_upvote(image_id, user_id).await.is_ok());
assert!(command.add_upvote(WRONG_UUID, user_id).await.is_err());
assert_eq!(number_of_votes(&pool, 1).await, upvotes + 1);
}
Expand All @@ -274,11 +284,42 @@ mod test {
let downvotes = number_of_votes(&pool, -1).await;
assert!(command.add_downvote(image_id, user_id).await.is_ok());
assert_eq!(number_of_votes(&pool, -1).await, downvotes + 1);
assert!(command.add_downvote(image_id, user_id).await.is_err());
assert!(command.add_downvote(image_id, user_id).await.is_ok());
assert!(command.add_downvote(WRONG_UUID, user_id).await.is_err());
assert_eq!(number_of_votes(&pool, -1).await, downvotes + 1);
}

#[sqlx::test(fixtures("meal", "image"))]
async fn test_override_votes(pool: PgPool) {
let command = PersistentCommandData { pool: pool.clone() };
let image_id = Uuid::parse_str("76b904fe-d0f1-4122-8832-d0e21acab86d").unwrap();
let user_id = Uuid::parse_str("00adb927-8cb9-4d80-ae01-d8f2e8f2d4cf").unwrap();

assert!(command.add_upvote(image_id, user_id).await.is_ok());
assert!(command.add_downvote(image_id, user_id).await.is_ok());

let vote = sqlx::query_scalar!(
"SELECT rating FROM image_rating WHERE image_id = $1 AND user_id = $2",
image_id,
user_id,
)
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(vote, -1);

assert!(command.add_upvote(image_id, user_id).await.is_ok());
let vote = sqlx::query_scalar!(
"SELECT rating FROM image_rating WHERE image_id = $1 AND user_id = $2",
image_id,
user_id,
)
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(vote, 1);
}

#[sqlx::test(fixtures("meal", "image"))]
async fn test_remove_upvote(pool: PgPool) {
let command = PersistentCommandData { pool: pool.clone() };
Expand Down
12 changes: 7 additions & 5 deletions backend/src/layer/data/flickr_api/api_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::layer::data::flickr_api::json_structs::{JsonRootError, JsonRootLicens
use axum::body::Bytes;
use reqwest::Response;
use serde::de::DeserializeOwned;
use tracing::trace;

pub struct ApiRequest {
api_key: String,
Expand Down Expand Up @@ -53,6 +54,7 @@ impl ApiRequest {
.bytes()
.await
.map_err(|e| ImageHosterError::DecodeFailed(e.to_string()))?;
trace!("successfully send request `{GET_SIZES}` to flickr for image {photo_id}");
Self::json_to_struct::<JsonRootSizes>(&bytes).map_or_else(
|_| Err(Self::determine_error(&bytes)),
|root| JsonParser::parse_get_sizes(&root, photo_id),
Expand All @@ -71,7 +73,7 @@ impl ApiRequest {
pub async fn flickr_photos_license_check(
&self,
photo_id: &str,
) -> Result<bool, ImageHosterError> {
) -> Result<(), ImageHosterError> {
let url = &format!(
"{BASE_URL}{GET_LICENCE_HISTORY}{TAG_API_KEY}{api_key}{TAG_PHOTO_ID}{photo_id}{FORMAT}",
api_key = self.api_key
Expand All @@ -82,9 +84,10 @@ impl ApiRequest {
.bytes()
.await
.map_err(|e| ImageHosterError::DecodeFailed(e.to_string()))?;
trace!("successfully send request `{GET_LICENCE_HISTORY}` to flickr for image {photo_id}");
Self::json_to_struct::<JsonRootLicense>(&bytes).map_or_else(
|_| Err(Self::determine_error(&bytes)),
|root| Ok(JsonParser::check_license(&root)),
JsonParser::check_license,
)
}

Expand Down Expand Up @@ -156,9 +159,8 @@ mod test {
async fn test_valid_check_license_request() {
let res = get_api_request()
.flickr_photos_license_check("52310534489")
.await
.unwrap();
assert!(!res);
.await;
assert!(res.is_err());
}

#[tokio::test]
Expand Down
Loading

0 comments on commit 97265ff

Please sign in to comment.