fix: added BASE_URL support

pull/1/head
parent 69b1601d92
commit 49632f1e2c

@ -0,0 +1,3 @@
BASE_URL=/
DATABASE_PATH=db.local.json

1
.gitignore vendored

@ -6,6 +6,7 @@ node_modules
dist
# Local files
.env
*.local*
# Editor directories and files

@ -7,6 +7,7 @@ Sito con una bacheca di problemi e la possibilità di inviare soluzioni in markd
Installare tutte le dipendenze con il package manager preferito di turno (ed in tal caso sostituire `npm` con ad esempio `pnpm` o `yarn`)
```bash shell
$ cp .env.development .env
$ npm install
```
@ -25,38 +26,4 @@ $ npm run serve
# TODO
- Pagina profilo utente
- Lista soluzioni inviate (con stato delle soluzioni: approvate o rifiutate)
- Pagina dell'admin
- Lista delle soluzioni non corrette
- Creazione nuovi problemi
- DBv2
```js
relations: [
{
from: 'Solution'
name: 'for'
to: 'Problem'
entries: [
['LRLAH2NoLFQAQHQgz', '1']
['JzyiDnwRCrkpzLL8W', '1']
['FFYMJjP2yr4ohdmdT', '2']
['VFHTb8fSrLOkPNVFx', '2']
]
}
{
from: 'User'
name: 'owns'
to: 'Solution'
entries: [
['aziis98', 'LRLAH2NoLFQAQHQgz']
['aziis98', 'JzyiDnwRCrkpzLL8W']
['BachoSeven', 'FFYMJjP2yr4ohdmdT']
['BachoSeven', 'VFHTb8fSrLOkPNVFx']
]
}
]
```
- [ ] Autenticazione vera magari con OAuth dell'ateneo o Google

@ -1,10 +1,14 @@
export function prependBaseUrl(route: string) {
return import.meta.env.BASE_URL + (route.startsWith('/') ? route.substring(1) : route)
}
export const server = {
async get(url: string) {
const res = await fetch(url, { credentials: 'include' })
const res = await fetch(prependBaseUrl(url), { credentials: 'include' })
return await res.json()
},
async post<T>(url: string, body?: T) {
const res = await fetch(url, {
const res = await fetch(prependBaseUrl(url), {
method: 'POST',
credentials: 'include',
headers: {
@ -16,7 +20,7 @@ export const server = {
return await res.json()
},
async patch<T>(url: string, body?: T) {
const res = await fetch(url, {
const res = await fetch(prependBaseUrl(url), {
method: 'PATCH',
credentials: 'include',
headers: {

@ -1,5 +1,6 @@
import { Link } from 'preact-router/match'
import { isAdministrator, User, UserRole } from '../../shared/model'
import { prependBaseUrl } from '../api'
const ROLE_LABEL: Record<UserRole, string> = {
['admin']: 'Admin',
@ -15,7 +16,7 @@ type Props = {
export const Header = ({ user, noLogin }: Props) => (
<header>
<div class="logo">
<a href="/">PHC / Problemi</a>
<a href={prependBaseUrl('/')}>PHC / Problemi</a>
</div>
<nav>
{user ? (

@ -1,3 +1,4 @@
import { prependBaseUrl } from '../api'
import { Markdown } from './Markdown'
type Props = {
@ -11,7 +12,7 @@ export const Problem = ({ id, content, solutionsCount }: Props) => {
<div class="problem">
<div class="problem-header">
<div class="problem-title">
<a href={`/problem/${id}`}>Problema {id}</a>
<a href={prependBaseUrl(`/problem/${id}`)}>Problema {id}</a>
</div>
</div>
<div class="problem-content">

@ -8,7 +8,7 @@ import {
SolutionStatus,
UserId,
} from '../../shared/model'
import { server } from '../api'
import { prependBaseUrl, server } from '../api'
import { Markdown } from './Markdown'
import { Select } from './Select'
@ -89,13 +89,14 @@ export const Solution = ({
{sentBy && (
<>
{' '}
di <a href={`/user/${sentBy}`}>@{sentBy}</a>
di <a href={prependBaseUrl(`/user/${sentBy}`)}>@{sentBy}</a>
</>
)}
{forProblem && (
<>
{' '}
per il <a href={`/problem/${forProblem}`}>Problema {forProblem}</a>
per il{' '}
<a href={prependBaseUrl(`/problem/${forProblem}`)}>Problema {forProblem}</a>
</>
)}
{!isNaN(d as any) && (

@ -1,7 +1,7 @@
import { StateUpdater, useEffect, useState } from 'preact/hooks'
import { createContext } from 'preact'
import { server } from './api'
import { prependBaseUrl, server } from './api'
import { User } from '../shared/model'
type Metadata = {
@ -13,9 +13,7 @@ export const MetadataContext = createContext<Metadata>({})
export const ServerContext = createContext<boolean>(false)
export const ClientContext = createContext<boolean>(false)
type CurrentUserHook = (
onLoaded?: (user: User | null) => void
) => [User | null, () => Promise<void>]
type CurrentUserHook = (onLoaded?: (user: User | null) => void) => [User | null, () => Promise<void>]
export const useCurrentUser: CurrentUserHook = onLoaded => {
const [user, setUser] = useState(null)
@ -49,7 +47,8 @@ export const useResource: ResourceHookFunction = (url, initialValue) => {
function refresh() {
const controller = new AbortController()
const realUrl = typeof url === 'function' ? url() : url
fetch(realUrl, { signal: controller.signal })
fetch(prependBaseUrl(realUrl), { signal: controller.signal })
.then(res => {
if (res.ok) {
return res.json()

@ -7,7 +7,8 @@ export const LoginPage = () => {
const [username, setUsername] = useState('')
const login = async () => {
await fetch(`/api/login`, {
// @ts-ignore
await fetch(`${import.meta.env.BASE_URL}/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -30,9 +31,7 @@ export const LoginPage = () => {
id="login-username"
type="text"
value={username}
onInput={e =>
setUsername(e.target instanceof HTMLInputElement ? e.target.value : '')
}
onInput={e => setUsername(e.target instanceof HTMLInputElement ? e.target.value : '')}
onKeyDown={e => e.key === 'Enter' && login()}
/>

@ -20,6 +20,7 @@
"body-parser": "^1.20.1",
"chalk": "^5.1.2",
"cookie-parser": "^1.4.6",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"http-status-codes": "^2.2.0",
"katex": "^0.16.3",

@ -11,6 +11,7 @@ specifiers:
chalk: ^5.1.2
concurrently: ^7.5.0
cookie-parser: ^1.4.6
dotenv: ^16.0.3
esbuild: ^0.15.13
express: ^4.18.2
http-status-codes: ^2.2.0
@ -38,6 +39,7 @@ dependencies:
body-parser: 1.20.1
chalk: 5.1.2
cookie-parser: 1.4.6
dotenv: 16.0.3
express: 4.18.2
http-status-codes: 2.2.0
katex: 0.16.3
@ -935,6 +937,11 @@ packages:
engines: {node: '>=0.3.1'}
dev: false
/dotenv/16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
dev: false
/ee-first/1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: false

@ -1,6 +1,8 @@
import path from 'path'
import fs from 'fs/promises'
import dotenv from 'dotenv'
import express, { Handler, Router } from 'express'
import morgan from 'morgan'
@ -9,6 +11,9 @@ import { createServer as createViteServer } from 'vite'
import { createApiRouter } from './server/routes'
import { RenderFunction } from './shared/ssr'
// Load ".env"
dotenv.config()
const HTML_ROUTES = ['/', '/login', '/problem/:id', '/admin', '/profile']
const config = {
@ -47,8 +52,7 @@ async function createDevRouter() {
const transformedTemplate = await vite.transformIndexHtml(req.originalUrl, indexHtml)
// Load (to be bundled) entry point for server side rendering
const render: RenderFunction = (await vite.ssrLoadModule('./client/entry-server.tsx'))
.default
const render: RenderFunction = (await vite.ssrLoadModule('./client/entry-server.tsx')).default
const { html, metadata } = render(req.originalUrl)
@ -58,9 +62,7 @@ async function createDevRouter() {
const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`
const metaTagsHtml =
'' +
(metadata.title
? `<meta property="og:title" content="${metadata.title}" />\n`
: '') +
(metadata.title ? `<meta property="og:title" content="${metadata.title}" />\n` : '') +
(metadata.description
? `<meta property="og:description" content="${metadata.description}" />\n`
: '') +
@ -91,10 +93,7 @@ async function createProductionRouter() {
r.use('/', express.static('dist/entry-client'))
mountIndexHtmlRoutes(r, async (req, res) => {
const transformedTemplate = await fs.readFile(
path.resolve('./dist/entry-client/index.html'),
'utf-8'
)
const transformedTemplate = await fs.readFile(path.resolve('./dist/entry-client/index.html'), 'utf-8')
const { html, metadata } = render(req.originalUrl)

@ -53,7 +53,7 @@ export async function createApiRouter() {
},
}
const db = createDatabase('./db.local.json', initialDatabaseValue)
const db = createDatabase(process.env.DATABASE_PATH ?? './db.local.json', initialDatabaseValue)
async function getRequestUser(req: Request) {
const userId = sessions.getUserForSession(req.cookies.sid)
@ -159,9 +159,7 @@ export async function createApiRouter() {
let solutions = await getSolutions(db)
// se l'utente non è loggato o se non è un amministratore allora mostra solo le soluzioni "visibili"
if (!requestUser || !isAdministrator(requestUser.role)) {
solutions = solutions.filter(
s => s.visible || (requestUser && s.sentBy === requestUser.id)
)
solutions = solutions.filter(s => s.visible || (requestUser && s.sentBy === requestUser.id))
}
// filtra rispetto agli utenti
if (queryUser !== null) {
@ -232,10 +230,7 @@ export async function createApiRouter() {
return
}
// uno studente può modificare solo il campo "content"
if (
user.role === 'student' &&
!validateObjectKeys<keyof SolutionModel>(req.body, ['content'])
) {
if (user.role === 'student' && !validateObjectKeys<keyof SolutionModel>(req.body, ['content'])) {
res.status(StatusCodes.UNAUTHORIZED)
res.send(`a student can only modify the field "content"`)
return

@ -17,7 +17,8 @@
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"jsxImportSource": "preact"
"jsxImportSource": "preact",
"types": ["vite/client"]
},
"include": ["client", "shared"],
"references": [{ "path": "./tsconfig.node.json" }]

@ -1,7 +1,10 @@
import { defineConfig } from 'vite'
import preactPlugin from '@preact/preset-vite'
import preact from '@preact/preset-vite'
console.log(`[Config] BASE_URL = "${process.env.BASE_URL}"`)
export default defineConfig({
plugins: [preactPlugin()],
base: process.env.BASE_URL,
plugins: [preact()],
})

Loading…
Cancel
Save