Skip to content
This repository has been archived by the owner on Feb 18, 2025. It is now read-only.

Commit

Permalink
Merge branch 'main' into setup-infra
Browse files Browse the repository at this point in the history
  • Loading branch information
amber committed Oct 2, 2024
2 parents c046330 + b894867 commit f554235
Show file tree
Hide file tree
Showing 24 changed files with 51 additions and 48 deletions.
Binary file modified .env.gpg
Binary file not shown.
4 changes: 2 additions & 2 deletions backend/src/main/java/edu/nus/market/dao/AccountDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public interface AccountDao {
"RETURNING id")
int updatePassword(int id, String passwordHash, String passwordSalt);

@Update("UPDATE account SET nickname = #{nickname}, avatar_url = #{avatar}, phone_code = #{phoneCode}, phone_number = #{phoneNumber}, preferred_currency = #{currency} WHERE id = #{id} AND deleted_at IS NULL" +
@Update("UPDATE account SET nickname = #{nickname}, avatar_url = #{avatar}, phone_code = #{phoneCode}, phone_number = #{phoneNumber}, preferred_currency = #{currency} WHERE id = #{id} AND deleted_at IS NULL " +
"RETURNING id")
int updateProfile(String nickname, String avatar, String phoneCode, String phone_number, String currency, int id);
int updateProfile(String nickname, String avatar, String phoneCode, String phoneNumber, String currency, int id);
}

10 changes: 4 additions & 6 deletions backend/src/main/java/edu/nus/market/security/CookieManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,18 @@ public class CookieManager {
public static ResponseCookie generateCookie(String accessToken){
ResponseCookie cookie = ResponseCookie.from("access_token", accessToken)
.httpOnly(true)
.secure(true)
.path("/")
.maxAge(7 * 24 * 60 * 60) // 1 week
.sameSite("Strict")
.maxAge(7 * 24 * 60 * 60)
.sameSite("Lax")
.build();
return cookie;
}
public static ResponseCookie deleteCookie(){
ResponseCookie cookie = ResponseCookie.from("access_token", null)
.httpOnly(true)
.secure(true)
.path("/")
.maxAge(0) // 1 week
.sameSite("Strict")
.maxAge(0)
.sameSite("Lax")
.build();
return cookie;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ResponseEntity<Object> getAccountService(int id) {
@Override
public ResponseEntity<Object> logoutService(){
ResponseCookie cookie = cookieManager.deleteCookie();
return ResponseEntity.status(HttpStatus.NO_CONTENT).header("Cookie", cookie.toString()).build();
return ResponseEntity.status(HttpStatus.NO_CONTENT).header("Set-Cookie", cookie.toString()).build();
}

/**
Expand All @@ -76,7 +76,7 @@ public ResponseEntity loginService(LoginReq loginReq){
String accessToken = jwtTokenManager.generateAccessToken(account.getId());
ResponseCookie cookie = cookieManager.generateCookie(accessToken);
// generate the JWTaccesstoken and send it to the frontend
return ResponseEntity.status(HttpStatus.CREATED).header("Cookie", cookie.toString()).body(new ResAccount(account));
return ResponseEntity.status(HttpStatus.CREATED).header("Set-Cookie", cookie.toString()).body(new ResAccount(account));
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorMsg(ErrorMsgEnum.WRONG_PASSWORD.ErrorMsg));
}
Expand All @@ -103,7 +103,7 @@ public ResponseEntity<Object> registerService(RegisterReq registerReq){
String accessToken = jwtTokenManager.generateAccessToken((accountId));
ResponseCookie cookie = cookieManager.generateCookie(accessToken);
// generate the JWTaccesstoken and send it to the frontend
return ResponseEntity.status(HttpStatus.CREATED).header("Cookie", cookie.toString()).body(new ResAccount(account));
return ResponseEntity.status(HttpStatus.CREATED).header("Set-Cookie", cookie.toString()).body(new ResAccount(account));
}

@Override
Expand Down
6 changes: 2 additions & 4 deletions docker-compose.dev.yaml
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
version: '3.8'

services:
frontend:
build:
context: frontend
dockerfile: Dockerfile
args:
NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL}
image: z1yoon/nus-secondhand-market-frontend:latest
ports:
- "80:3000"
restart: always
environment:
- API_BASE_URL=${API_BASE_URL}
user: root

backend:
build:
context: backend
dockerfile: Dockerfile
image: z1yoon/nus-secondhand-market-backend:latest
ports:
- "8081:8081"
restart: always
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
version: '3.8'

services:
frontend:
image: z1yoon/nus-secondhand-market-frontend:latest
ports:
- "80:3000"
restart: always
environment:
- API_BASE_URL=${API_BASE_URL}
user: root

backend:
Expand Down
3 changes: 1 addition & 2 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# The base URL of backend API entrypoint.
# For example, "http://localhost:8080/",
# Note: A trailing slash is mandatory.
# For example, "http://localhost:8080",
NEXT_PUBLIC_API_BASE_URL=
2 changes: 0 additions & 2 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=build --chown=nextjs:nodejs /app/public public
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone .
COPY --from=build --chown=nextjs:nodejs /app/.next/static .next/static

USER nextjs

Expand Down
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"packageManager": "pnpm@9.11.0",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"build": "next build && cp -r .next/static .next/standalone/.next && cp -r public .next/standalone",
"start": "node .next/standalone/server.js",
"lint": "tsc && eslint . --cache --cache-location node_modules/.cache/eslint/.eslint-cache"
},
"dependencies": {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/app/login/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ export function LoginForm() {
string,
FormEvent<HTMLFormElement>
>(
"auth/me",
"/auth/me",
async (_, { arg: event }) => {
event.preventDefault();

const formData = Object.fromEntries(new FormData(event.currentTarget));

const { email, password } = v.parse(formSchema, formData);

return await new ClientRequester().post<Account>("auth/token", {
return await new ClientRequester().post<Account>("/auth/token", {
email,
password,
});
Expand All @@ -58,6 +58,7 @@ export function LoginForm() {
description: `Welcome back, ${account.nickname ?? account.email}!`,
});
router.push("/");
router.refresh();
},
throwOnError: false,
onError: (error) => {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/app/register/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function RegisterForm() {
string,
FormEvent<HTMLFormElement>
>(
"auth/me",
"/auth/me",
async (_, { arg: event }) => {
event.preventDefault();

Expand All @@ -52,7 +52,7 @@ export function RegisterForm() {
throw new Error("Passwords do not match. Please double check.");
}

return await new ClientRequester().post<Account>("auth/me", {
return await new ClientRequester().post<Account>("/auth/me", {
email,
password,
});
Expand All @@ -66,6 +66,7 @@ export function RegisterForm() {
description: `Welcome on board, ${account.email}!`,
});
router.push("/");
router.refresh();
},
throwOnError: false,
onError: (error) => {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/app/settings/cards/delete-account-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ function DeleteAccountButton() {
string,
MouseEvent<HTMLButtonElement>
>(
"auth/me",
"/auth/me",
async () => {
return await new ClientRequester().delete<undefined>("auth/me");
return await new ClientRequester().delete<undefined>("/auth/me");
},
{
populateCache: true,
Expand All @@ -109,6 +109,7 @@ function DeleteAccountButton() {
"We are sorry to see you go. 🥲 Remember you can contact our support team to find your account back in the next 30 days!",
});
router.push("/");
router.refresh();
},
throwOnError: false,
onError: (error) => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/settings/cards/update-email-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ export function UpdateEmailCard({ initialEmail }: Props) {
string,
FormEvent<HTMLFormElement>
>(
"auth/me",
"/auth/me",
async (_, { arg: event }) => {
event.preventDefault();

const formData = Object.fromEntries(new FormData(event.currentTarget));

const { email } = v.parse(formSchema, formData);

return await new ClientRequester().patch<Account>("auth/me", {
return await new ClientRequester().patch<Account>("/auth/me", {
email,
});
},
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/settings/cards/update-nickname-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ export function UpdateNicknameCard({ initialNickname }: Props) {
string,
FormEvent<HTMLFormElement>
>(
"auth/me",
"/auth/me",
async (_, { arg: event }) => {
event.preventDefault();

const formData = Object.fromEntries(new FormData(event.currentTarget));

const { nickname } = v.parse(formSchema, formData);

return await new ClientRequester().patch<Account>("auth/me", {
return await new ClientRequester().patch<Account>("/auth/me", {
nickname,
});
},
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/settings/cards/update-whatsapp-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ export function UpdateWhatsappCard({
string,
FormEvent<HTMLFormElement>
>(
"auth/me",
"/auth/me",
async (_, { arg: event }) => {
event.preventDefault();

const formData = Object.fromEntries(new FormData(event.currentTarget));

const { phoneCode, phoneNumber } = v.parse(formSchema, formData);

return await new ClientRequester().patch<Account>("auth/me", {
return await new ClientRequester().patch<Account>("/auth/me", {
phone_code: phoneCode,
phone_number: phoneNumber,
});
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/settings/contacts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ServerRequester } from "@/utils/requester/server";
import { UpdateWhatsappCard } from "../cards/update-whatsapp-card";

export default async function ContactsSettingsPage() {
const me = await new ServerRequester().get<Account | undefined>("auth/me");
const me = await new ServerRequester().get<Account | undefined>("/auth/me");

return (
<div className="grid gap-6">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/settings/display/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ServerRequester } from "@/utils/requester/server";
import { UpdateNicknameCard } from "../cards/update-nickname-card";

export default async function DisplaySettingsPage() {
const me = await new ServerRequester().get<Account | undefined>("auth/me");
const me = await new ServerRequester().get<Account | undefined>("/auth/me");

return (
<div className="grid gap-6">
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DeleteAccountCard } from "./cards/delete-account-card";
import { UpdateEmailCard } from "./cards/update-email-card";

export default async function SettingsPage() {
const me = await new ServerRequester().get<Account | undefined>("auth/me");
const me = await new ServerRequester().get<Account | undefined>("/auth/me");

return (
<div className="grid gap-6">
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/framework/me-card/log-out-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export function LogOutButton() {
string,
MouseEvent<HTMLButtonElement>
>(
"auth/me",
"/auth/me",
async () => {
return await new ClientRequester().delete<undefined>("auth/token");
return await new ClientRequester().delete<undefined>("/auth/token");
},
{
populateCache: true,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/framework/me-card/me-card-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ type Props = {

export function MeCardClient({ initialMe, fallback }: Props) {
const { data: me } = useSWR(
"auth/me",
"/auth/me",
async () => {
return await new ClientRequester().get<Account>("auth/me");
return await new ClientRequester().get<Account>("/auth/me");
},
{
fallbackData: initialMe as Account,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { JoinNowCard } from "./join-now-card";
import { MeCardClient } from "./me-card-client";

export async function MeCardServer() {
const me = await new ServerRequester().get<Account | undefined>("auth/me");
const me = await new ServerRequester().get<Account | undefined>("/auth/me");

return <MeCardClient initialMe={me} fallback={<JoinNowCard />} />;
}
7 changes: 5 additions & 2 deletions frontend/src/utils/requester/client-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import type { Fetcher } from "./fetcher";

export class ClientFetcher implements Fetcher {
public async fetch<T>(endpoint: string, init: RequestInit = {}) {
const url = new URL(endpoint, process.env["NEXT_PUBLIC_API_BASE_URL"]);
const url = process.env["NEXT_PUBLIC_API_BASE_URL"] + endpoint;

const response = await fetch(url, init);
const response = await fetch(url, {
...init,
credentials: "include",
});

if (response.status === 204) {
return undefined as never;
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/utils/requester/server-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import type { Fetcher } from "./fetcher";

export class ServerFetcher implements Fetcher {
public async fetch<T>(endpoint: string, init: RequestInit = {}) {
const url = new URL(endpoint, process.env["NEXT_PUBLIC_API_BASE_URL"]);
const url = process.env["API_BASE_URL"] + endpoint;

const response = await fetch(url, {
...init,
headers: {
...init.headers,
credentials: "include",
Cookie: cookies().toString(),
},
});
} as RequestInit);

if (response.status === 204) {
return undefined as never;
Expand Down
5 changes: 4 additions & 1 deletion init/1-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ create table account (
-- Department or school of the user.
department_id int default null references department(id) on delete set null,

-- User's WhatsApp phone country code.
phone_code text default null,

-- User's WhatsApp phone number.
phone text default null,
phone_number text default null,

-- User's preferred display currency, in the form of ISO 4217, e.g. CNY, SGD.
preferred_currency text default null,
Expand Down

0 comments on commit f554235

Please sign in to comment.