diff --git a/.envrc b/.envrc index 4f0af3a80ef9b7d25eaa8177ee64bcd7310af8b4..3b5bb1416c85df12ee45836120a60c4dcab29a79 100644 --- a/.envrc +++ b/.envrc @@ -7,6 +7,3 @@ fi # loads personal (secret) data from separate env file (not checked in) source_env_if_exists .envrc.local - -type yarn >/dev/null 2>&1 && PATH="$PATH:$(yarn global bin)" -export PATH \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a05bc5929aeba22e57414b8455631cb825ae132e..7ac02c1134466889944302a2290485dc9fdc75bd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ default: - env interruptible: true tags: - - holi-small # build on smaller machine + - 1cpu-4gb # build on smaller machine variables: API_DOMAIN_PATH: "$CI_PROJECT_DIR/api_domain" diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index c1d699695f34ed0a0d789b795412ef67013e7458..0000000000000000000000000000000000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -gitleaks protect --staged -v -c ../.gitleaks.toml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2f34ebc58adb3df395e52bedee0d670532274122 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,9 @@ +repos: +- repo: local + hooks: + - id: gitleaks + name: gitleaks + language: system + entry: gitleaks protect --staged -v -c ../.gitleaks.toml + pass_filenames: false + always_run: true diff --git a/app/common_test.ts b/app/common_test.ts index 7c29193eb1df30dff9cee182602c8027ca998ca5..ddbf135ffc9d7859ded40537c03cc63eb7db13d9 100644 --- a/app/common_test.ts +++ b/app/common_test.ts @@ -49,6 +49,7 @@ export const processGqlRequest = ( headers: { "content-type": "application/json", "accept": "*/*", + ...headers, }, body: JSON.stringify({ operationName: null, diff --git a/app/goodnews.ts b/app/goodnews.ts index c2554a5aa5a41fcbffe96d8a910fb82339022960..b0564b883b2a53ba14b6eb44b5738e110b7bc1ed 100644 --- a/app/goodnews.ts +++ b/app/goodnews.ts @@ -92,7 +92,7 @@ export const toGoodNewsArticle = ( const fetchPage = (config: ContentfulConfig) => - (skip: number, limit: number): Promise<ContentfulEntriesResponse> => { + async (skip: number, limit: number): Promise<ContentfulEntriesResponse> => { // baseUrl used for logging to not leak access_token const baseUrl = `${config.baseUrl}/${config.spaceId}/environments/${config.environment}/entries`; @@ -105,13 +105,17 @@ const fetchPage = logger.info(`fetching articles ${skip} to ${skip + limit} from ${baseUrl}`); const start = Date.now(); - return fetch(url) - .then((result) => result.json()) - .then((result) => { - const duration = Date.now() - start; - logger.debug(`fetching articles took ${duration} ms`); - return result; - }); + try { + const fetchResult = await fetch(url); + const resultJson = await fetchResult.json(); + return resultJson; + } catch (err) { + logger.error("fetching articles failed: " + err.message); + throw err; + } finally { + const duration = Date.now() - start; + logger.info(`fetching articles took ${duration} ms`); + } }; type ContentfulItemFilter = (item: ContentfulItem) => boolean; diff --git a/app/server.ts b/app/server.ts index 3775eab303e25caf5cede53beadc7de792784f67..600b36950a140b7e465b4c77a7d614b028d138a0 100644 --- a/app/server.ts +++ b/app/server.ts @@ -61,19 +61,25 @@ const createResolvers = (config: ContentfulConfig) => { ): Promise<GoodNewsArticleQueryResponse> => { return config.fake ? Promise.resolve({ articles: [], previousDateTeaser: undefined }) - : executeArticlesQuery(config)(parameters, context.language); + : executeArticlesQuery(config)( + parameters, + getLanguage(context?.request?.headers), + ); }, }, }); }; -const getLanguage = (languages = "") => - languages +const getLanguage = (headers?: Headers) => { + const languages: string = headers?.get("accept-language") || DEFAULT_LANGUAGE; + return (languages .split(",") // languages are comma separated .map((language) => language.split("-")[0]) // languages may include country code 'de-DE' e.g. .map((language) => language.split(";")[0]) // languages may include a 'de;q=0.6' e.g. + .map((language) => language.trim()) .find((language) => SUPPORTED_LANGUAGES.includes(language)) || - DEFAULT_LANGUAGE; + DEFAULT_LANGUAGE) as GoodNewsLanguage; +}; export const DEFAULT_PORT = 8002; export const DEFAULT_CACHE_ENABLED = true; @@ -103,10 +109,7 @@ export const createGraphQLServer = (config: ServerConfig): GraphQLServer => { ? [ useResponseCache({ // global cache per language, shared by all users - session: (request: Request) => - getLanguage( - request.headers.get("accept-language") || DEFAULT_LANGUAGE, - ), + session: (request: Request) => getLanguage(request.headers), ttlPerSchemaCoordinate: { "Query.articles": config.cacheTtlMs || DEFAULT_CACHE_TTL_MS, }, @@ -118,14 +121,6 @@ export const createGraphQLServer = (config: ServerConfig): GraphQLServer => { schema: createSchema({ resolvers, typeDefs }), graphiql: true, plugins, - context: (context: GraphQLContext) => { - const headers = new Headers(context.params?.extensions?.headers); - const languages = headers.get("accept-language") || DEFAULT_LANGUAGE; - return { - ...context, - language: getLanguage(languages), - }; - }, }); }; @@ -133,6 +128,7 @@ export const createGraphQLServer = (config: ServerConfig): GraphQLServer => { export type GraphQLServer = any; export type GraphQLContext = { + request?: { headers: Headers }; params?: { extensions?: { headers?: { @@ -140,12 +136,11 @@ export type GraphQLContext = { }; }; }; - language: GoodNewsLanguage; }; export const startServer = (config: ServerConfig): Promise<void> => { const graphQLServer: GraphQLServer = createGraphQLServer(config); - return serve(graphQLServer.handleRequest, { + return serve(graphQLServer, { port: config.port || DEFAULT_PORT, onListen({ port, hostname }) { logger.info( diff --git a/terraform/environments/deployment.tf b/terraform/environments/deployment.tf index 1d1db0e18b99fafd36ea4fa7d8b9261faf721305..83caa0b21044c47a22d7ff529c190ee897e269e4 100644 --- a/terraform/environments/deployment.tf +++ b/terraform/environments/deployment.tf @@ -54,8 +54,7 @@ resource "google_cloud_run_service" "goodnews_api" { } env { name = "CACHE_TTL_MS" - value = local.environment == "production" ? "43200000" : "60000" - # 12 hours for production, otherwise 1 minute + value = local.environment == "production" ? "43200000" : "43200000" } env { name = "CONTENTFUL_BASE_URL" @@ -100,7 +99,7 @@ resource "google_cloud_run_service" "goodnews_api" { memory = local.environment == "production" ? "512Mi" : "256Mi" } requests = { - cpu = local.environment == "production" ? "1000m" : "10m" + cpu = local.environment == "production" ? "1000m" : "1000m" memory = local.environment == "production" ? "512Mi" : "256Mi" } }